Implementing a NamingStrategy in Hibernate 5 (make autogenerated column names UPPERCASE) - java

I need to let Hibernate auto generate the database starting from the entities, however I want them all UPPERCASE.
This used to work in the past now I have messed up column names with Upper and Lower case letters.
I enabled the
.setProperty("hibernate.hbm2ddl.auto", "create")
to let Hibernate auto generate the database, and I created an UppercaseNamingStrategy.java extending org.hibernate.cfg.ImprovedNamingStrategy.
According to https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html_single/#configuration-namingstrategy
Now I should
You can specify a different strategy by calling
Configuration.setNamingStrategy() before adding mappings:
SessionFactory sf = new Configuration()
.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
.addFile("Item.hbm.xml")
.addFile("Bid.hbm.xml")
.buildSessionFactory();
org.hibernate.cfg.ImprovedNamingStrategy is a built-in strategy that
might be a useful starting point for some applications.
Configuration.setNamingStrategy() however seems to be no more in Hibernate 5.0.6.
I would of course like to do it programatically (I don't have .xml configuration files and don't want them).
NOTE:
Using
.setProperty("hibernate.ejb.naming_strategy", "my.project.hibernate.UppercaseNamingStrategy")
doesn't work as well, seems to be ignored altogether...

Hibernate 5 uses two new interfaces for name strategies PhysicalNamingStrategy and ImplicitNamingStrategy. You need just implement PhysicalNamingStrategy. It is called by Hibernate after all column names are created for model. So you can just make it uppercase. Hibernate uses by default PhysicalNamingStrategyStandardImpl, that do nothing. You can just extend it
public class UpperCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl {
#Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
StringUtils.upperCase(name.getText(), Locale.ENGLISH));
}
}
You can build session factory with UpperCaseNamingStrategy by this way
Configuration configuration = new Configuration();
configuration.setPhysicalNamingStrategy(new UpperCaseNamingStrategy());
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
I am working on a more complex name strategy now. You can refer Hibernate5NamingStrategy if you are interested.

Related

Hibernate Implicit Naming Strategy is ignored when using multiple datasources

According to the following tutorial, I created two configruation files for two database sources: Baeldung-JPA-Multiple-Databases
It works fine, but it seems that the implicit naming strategy is ignored. That means that an Entity CustomerContact is not mapped to a table customer_contact.
I tried to add the following two properties, but nothing changes:
public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
...
properties.put("hibernate.naming.implicit-strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
properties.put("hibernate.naming.physical-strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
em.setJpaPropertyMap(properties);
...
}
One solution would be to use the name attribute of the persistence annotations, like
#Entity(name = "customer_contact"), but I would like to avoid that and use the implicit mapping instead.
The answer can be found here: Can't set JPA naming strategy after configuring multiple data sources (Spring 1.4.1 / Hibernate 5.x)
The needed properties are:
props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());

Dynamic schema in Hibernate #Table Annotation

Imagine you have four MySQL database schemas across two environments:
foo (the prod db),
bar (the in-progress restructuring of the foo db),
foo_beta (the test db),
and bar_beta (the test db for new structures).
Further, imagine you have a Spring Boot app with Hibernate annotations on the entities, like so:
#Table(name="customer", schema="bar")
public class Customer { ... }
#Table(name="customer", schema="foo")
public class LegacyCustomer { ... }
When developing locally it's no problem. You mimic the production database table names in your local environment. But then you try to demo functionality before it goes live and want to upload it to the server. You start another instance of the app on another port and realize this copy needs to point to "foo_beta" and "bar_beta", not "foo" and "bar"! What to do!
Were you using only one schema in your app, you could've left off the schema all-together and specified hibernate.default_schema, but... you're using two. So that's out.
Spring EL--e.g. #Table(name="customer", schema="${myApp.schemaName}") isn't an option--(with even some snooty "no-one needs this" comments), so if dynamically defining schemas is absurd, what does one do? Other than, you know, not getting into this ridiculous scenario in the first place.
I have fixed such kind of problem by adding support for my own schema annotation to Hibernate. It is not very hard to implement by extending LocalSessionFactoryBean (or AnnotationSessionFactoryBean for Hibernate 3). The annotation looks like this
#Target(TYPE)
#Retention(RUNTIME)
public #interface Schema {
String alias() default "";
String group() default "";
}
Example of using
#Entity
#Table
#Schema(alias = "em", group = "ref")
public class SomePersistent {
}
And a schema name for every combination of alias and group is specified in a spring configuration.
you can try with interceptors
public class CustomInterceptor extends EmptyInterceptor {
#Override
public String onPrepareStatement(String sql) {
String prepedStatement = super.onPrepareStatement(sql);
prepedStatement = prepedStatement.replaceAll("schema", "Schema1");
return prepedStatement;
}
}
add this interceptor in session object as
Session session = sessionFactory.withOptions().interceptor(new MyInterceptor()).openSession();
so what happens is when ever onPrepareStatement is executed this block of code will be called and schema name will be changed from schema to schema1.
You can override the settings you declare in the annotations using a orm.xml file. Configure maven or whatever you use to generate your deployable build artifacts to create that override file for the test environment.

How do I configure JPA table name at runtime?

I have an issue where I have only one database to use but I have multiple servers where I want them to use a different table name for each server.
Right now my class is configured as:
#Entity
#Table(name="loader_queue")
class LoaderQueue
I want to be able to have dev1 server point to loader_queue_dev1 table, and dev2 server point to loader_queue_dev2 table for instance.
Is there a way i can do this with or without using annotations?
I want to be able to have one single build and then at runtime use something like a system property to change that table name.
For Hibernate 4.x, you can use a custom naming strategy that generates the table name dynamically at runtime. The server name could be provided by a system property and so your strategy could look like this:
public class ServerAwareNamingStrategy extends ImprovedNamingStrategy {
#Override
public String classToTableName(String className) {
String tableName = super.classToTableName(className);
return resolveServer(tableName);
}
private String resolveServer(String tableName) {
StringBuilder tableNameBuilder = new StringBuilder();
tableNameBuilder.append(tableName);
tableNameBuilder.append("_");
tableNameBuilder.append(System.getProperty("SERVER_NAME"));
return tableNameBuilder.toString();
}
}
And supply the naming strategy as a Hibernate configuration property:
<property
name="hibernate.ejb.naming_strategy"
value="my.package.ServerAwareNamingStrategy"
/>
I would not do this. It is very much against the grain of JPA and very likely to cause problems down the road. I'd rather add a layer of views to the tables providing unified names to be used by your application.
But you asked, so have some ideas how it might work:
You might be able to create the mapping for your classes, completely by code. This is likely to be tedious, but gives you full flexibility.
You can implement a NamingStrategy which translates your class name to table names, and depends on the instance it is running on.
You can change your code during the build process to build two (or more) artefacts from one source.

hiberate 4.3 - entity class instances not returned in list

I am using Hibernate 4.3.8 without Spring. I am using Hibernate's session API. I have one entity class Category which I have annotated properly with #Entity, #Table, #Id, #Column and so on. I don't use .hbm.xml descriptor files, I just want to use the annotations in my domain/entity java source/class files.
1) OK, I create my hibernate SessionFactory in this way:
Configuration configuration = new Configuration().configure();
// configuration.addClass(Category.class);
StandardServiceRegistryBuilder builder =
new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
configuration.addPackage("com.test.db.domain");
factory = configuration.buildSessionFactory(builder.build());
2) Then when I try this:
session.createCriteria(Category.class).list();
I just get an empty list back (I was expecting to get all categories that are in the DB table). The Category class is in the com.test.db.domain package.
What could be the reason for this? I am stuck for almost a day on this.
3) Note that if I use session.createSQLQuery I can connect to my DB and get all categories.
4) Also note that I don't want to use Hibernate's EntityManager API, JPA, and the XML descriptor(s) related to JPA.
The problem turned out to be the following: calling configuration.addPackage does not do what I initially expected it to do. See also:
hibernate 4.3.x - load all entity annotated classes
Instead of calling addPackage what one should do is to call configuration.addAnnotatedClass for each class from his own entity/domain classes.

JPA/Hibernate switch schema programmatically

I'm working at the moment on a new project which has following requirement:
Several database schemas are holding the same tables with identical structure (in short: one entity for multiple schemas).
Is it possible to switch between those schemas by code? Want I want to achieve is:
User selects schema B and updates some entities in this. After this he does a insert in schema A and so on. I know that I could do this by basic JDBC by providing the schema to the statements but if I can avoid that I would do so.
Maybe some other java ORM can do this? I'm only familiar with JPA / Hibernate.
Regards
You can use separate SessionFactorys or EntityManagerFactorys, one for each schema.
Since you said that the user selects schema A or B, you can use something like this:
public enum Schema {
A, B
}
public EntityDaoImpl {
// Create and populate the map at DAO creation time (Spring etc.).
private Map<Schema, SessionFactory> sessionFactoryBySchema = ...;
private Session getSession(Schema schema) {
SessionFactory sessionFactory = sessionFactoryBySchema.get(schema);
return sessionFactory.getCurrentSession(); // ... or whatever
}
public void saveEntity(Schema schema, Entity entity) {
getSession(schema).save(entity);
}
}

Categories