JPA, externalizing column, table or schema names? - spring

I adapt Spring+Hibernate+Tomcat+Oracle application to distributed form and need to make schema names customizable in JPA annotations, something like with Spring EL:
#Entity
#Table(name = "LOSS", schema="${app.dataSchema}")
public class Loss { ... }
I look for support of placeholders in annotation like ${app.dataSchema} in above example. Or any other possibility...
Idea - to have application that can be deployed on another site without recompilation, where preserved DB relations/hierarchy, but some names are changed and can be configured by system properties, JNDI or deploy context descriptor.
One solution that I see - to create view in selected schema - to provide bridge between actual schema/table/column and application hard-coded names.
But I hope that JPA/Hibernate/Spring have some instruments for such configuration...

Use orm.xml. That is what JPA provides it for. Putting deployment info in annotations is a bad idea if you ever need to change deployment, so just having a different orm.xml file means you can easily achieve that. Note that you can set a default schema in the persistence-unit-defaults part of orm.xml too

Externalize Schema
By EntityManagerFactory
<bean id="domainEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="pun"/>
<property name="dataSource" ref="domainDataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false"/>
<property name="showSql" value="false"/>
<property name="databasePlatform" value="${hibernate.dialect}"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create-update</prop>
<prop key="hibernate.default_schema">${yourSchema}</prop>
</props>
</property>
</bean>
By orm.xml
...
<persistence-unit name="MySchemaPU" transaction-type="JTA">
<provider>
<mapping-file>META-INF/orm.xml</mapping-file>
...
orm.xml below:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<schema>myschema</schema>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
By persistence.xml
<persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>myDatasource</jta-data-source>
<properties>
.....
<property name="hibernate.default_schema" value="MYSCHEMA"/>
</properties>
</persistence-unit>
For Columns and Tables the deployment must be changed. You cannot replace placeholders at runtime in compiled classes.

Hibernate has org.hibernate.cfg.Configuration which is a point where packages are scanned and config files are read.
It uses org.hibernate.cfg.AnnotationBinder.bindClass(...) which read annotations on classes.
These classes (8 kLOC) and methods so long that I can't read them easy so I have no hope to subclass Configuration...
Ever if note that all classes and initialisation code binded to concrete classes without possibility to inject own, seems my task can't be solved easy with current Hibernate 4.x architecture.
I start looking for alternatives and ask about externaliztion of config in EBean mailing list and got answer:
public class MySchemaNamingConvention
extends com.avaje.ebean.config.UnderscoreNamingConvention {
#Override
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
final Table t = findTableAnnotation(beanClass);
if (t != null && !isEmpty(t.name())) {
String catalog = t.catalog();
String schema = t.schema();
////// CUSTOM schema processing code HERE.
////// CUSTOM schema processing code HERE.
////// CUSTOM schema processing code HERE.
return new TableName(quoteIdentifiers(catalog),
quoteIdentifiers(schema),
quoteIdentifiers(t.name()));
}
return null;
}
So damn easy that I say good bye to Hibernate!
UPDATE #Xstian. Before I migrate to Ebean I set default schema through hibernate.default_schema and extensively use views and synonyms to control which table will be accessible (Oracle):
grant select on ANOTHER_SCHEMA.TBL to MY_SCHEMA;
create or replace view MY_SCHEMA.TBL as select * from ANOTHER_SCHEMA.TBL;
create or replace synonym MY_SCHEMA.TBL for ANOTHER_SCHEMA.TBL;
During migration to views/synonyms I have issue only with DB constraints.
In case if you use Oracle - FK constraint on view not work unless you add constraint with disable novalidate keyword and same to referenced PK:
alter view MY_SCHEMA.XXX add constraint PK_XXX primary key(ID) disable novalidate;
alter table/view MY_SCHEMA.TBL add constraint FK_XXX foreign key (XXX_ID)
references MY_SCHEMA.XXX (ID) disable novalidate;
And synonyms don't allow FK constraint at all for Oracle!
Seems that Hibernate dictate of data layout and I think to switch to more flexible framework - Ebean, but I also evaluate sql2o and jOOQ.

Related

Hibernate generates too long identifiers for Oracle

We are using JPA with Hibernate 4.3.8 and Oracle 11. Our entities doesn't have explicit DB identifier names declared in annotations and so we rely upon Hibernate to generate them correctly.
For MySQL it works properly but after we had switched to Oracle then we've encountered couple of the problems.
One of them is that Hibernate-generated schema contains identifiers longer than Oracle supports. We thought that Oracle10gDialect handles it but it seems we were wrong.
What is the best approach to add support of Oracle to our application? Must we explicitly declare all the database tables/columns/indexes... in annotations (#Table, #Column...)? Shouldn't it be Hibernate who takes a care of this dialect-specific task?
Another problem is that Hibernate also doesn't escape the keywords (eg. column name code must be escaped in Oracle). What if we decide to support another database in the future? Must we choose all the identifier names so that all of them suit for every DB? This seems to be very overhelming. (Note that the property hibernate.globally_quoted_identifiers can solve it partially but then #UniqueConstraint columns doesn't match.)
Or maybe... does Eclipselink handle this?
In such cases you can use Hibernate's NamingStrategy prepared for Oracle.
In simple words: NamingStrategy tells Hibernate how to generate names for columns or tables.
You can provide oracle-aware NamingStrategy as I do for each of my Oracle/Hibernate projects.
Here is sample JPA configuration:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
...
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.ejb.naming_strategy" value="com.mycompany.OracleNamingStrategy"/>
<entry key="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
...
</map>
</property>
</bean>
Here is the NamingStrategy I have been using for many projects:
https://code.google.com/p/hibernate-naming-strategy-for-oracle/source/browse/trunk/src/de/schauderhaft/hibernate/OracleNamingStrategy.java
look at smart abbreviating method that guarantees to produce Oracle-compliant name:
public static String abbreviateName(String someName) {
if (someName.length() <= MAX_LENGTH)
return someName;
String[] tokens = splitName(someName);
shortenName(someName, tokens);
return assembleResults(tokens);
}
There are many articles about applying NamingStratety with Hibernate, for example:
1.
http://www.petrikainulainen.net/programming/tips-and-tricks/implementing-a-custom-namingstrategy-with-hibernate/
2.
http://sudhirmongia.blogspot.com/2010/08/naming-strategy-in-hibernate.html
I hope this helps.

Scriptella - Spring Integration - DB Upgrade How To?

I am currently using Scriptella ETL in Spring. I want to change the db schema and perform an upgrade of the db when i execute my program. I did not find any reference to do this except for a way using ANT.
db-upgrade example
Wanted to know if we can write a separate sql like v1-v2.xml with the new alters and create statements and have it referenced in spring context.xml?
My current spring context xml referencing etl..
<bean id="etlProgress" class="scriptella.interactive.ConsoleProgressIndicator"/>
<bean id="etlExecutor" class="scriptella.driver.spring.EtlExecutorBean">
<property name="configLocation" value="etl.xml"/>
<property name="progressIndicator"><ref local="etlProgress"/></property>
</bean>
Any help here pls?
Found an answer myself. In etl.xml you can add a query that queries the "MetaInf" table. In my case, I had a metainf table like this.
CREATE TABLE Metainf (
buildnum int primary key
);
INSERT INTO Metainf VALUES (1);
That means, i have to query "buildnum" to check the version of the db. This can be done the following way
<query>
<!-- Selects current DB build -->
SELECT * FROM Metainf
<!-- Check if upgrade is necessary -->
<script if="buildnum lt 1">
<!--Upgrades DB to build 1 -->
<!--...-->
<!-- Update Metainf to confirm successful upgrade -->
UPDATE Metainf SET buildnum=1;
</script>
<script if="buildnum eq 1">
<!-- upgrade scripts for subsequent builds -->
UPDATE Metainf SET buildnum=2;
</script>
</query>
I placed my old db file and ran after this change. Voila! I have my new changes in the database.
People who are looking for this answer, do try and let me know if it worked for you also.
Thanks

Spring jpa modifies column names

I'm using spring-jpa and I give column names to entities like;
#Column(name="UserDetail")
But it looks for user_detail. How can I give custom column names?
To change the naming strategy used by hibernate :
If you have a persistence.xml file, then change
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
to
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy" />

Hibernate Criteria list doesn't return new records immediate

I'm using Spring MVC with Hibernate 4.
My problem is when I use Criteria with list() method to fetch all records from the table. There is a 3rd party process which inserts records to the table every 1 second, and a screen which represent this table as is each time the user press "Refresh" button. When I use the Criteria.list() on the same session I got records with delay of 45 secs. minimum. Which means the user see records which inserted only before 45 secs.
This is criteria use:
Criteria crit = getCurrentSession().createCriteria(Audit.class);
return ((List<Audit>) crit.list());
This is hibernate + transaction settings:
Session Factory:
<bean id="auditsSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="abstractDataSource"/>
<property name="packagesToScan" value="com.java.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<!-- Only for debug -->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
</props>
</property>
</bean>
Transcation Manager:
<bean id="auditsTxManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="auditsSessionFactory"/>
</bean>
#Transactional setting:
#Transactional(isolation = Isolation.READ_COMMITTED, value = "auditsTxManager")
Also, I'm using: OpenSessionInViewFilter
I've already tried to:
Disable query cache
Change Isolation level to higher one
clear() session
Any suggestions?
Thanks,
Tal.
Query cache is not relevant as this uses criteria.
There is a session cache, do you create a new session with each request ?
Are you sure the inserts are inserting new data correctly/and not a problem with the web page displaying correctly. And there is no 2nd level cache ?

How can i add sample data to an edmx file and display it in Visual-Studio

I am using Visual Studio 2010 and ADO.Net Entity Data Model to create my database schema. Since i am still drafting and discussing the schema i would like to be able to add example data to the edmx-file and be able to show it in the designer like in the following screenshot:
Source code of EDMX file with added sample data
The underlying xml-code would contain the normal edmx-output and on top the sample data like below:
<?xml version="1.0" encoding="utf-8"?>
....
<EntityType Name="Title">
<Documentation>
<Summary>Table titles contains all the dvd-title we have in our store</Summary>
</Documentation>
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32"
Name="Id" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Title" Nullable="false" >
<Documentation>
<Summary>The title of the movie</Summary>
<LongDescription>
The translated title of the movie and the original title
</LongDescription>
</Documentation>
</Property>
</EntityType>
<Titles>
<Title>
<id>810</id>
<title>Pulp Fiction</title>
<year>1994</year>
</Title>
<Title>
<id>940</id>
<title>Lock, Stock and Two Smoking Barrels</title>
<year>1998</year>
</Title>
</Titles>
The sample data should be displayed and should be editable in design view.
Does anyone know a tool or a way to combine the edmx-file and to display and add sample data?
Bonus Question: When i generate the sql-statement the documentation is not added to the sql statement. Is there a way to include the documentation of the fields and the tables so that they would be added to Microsoft SQL Server?
I don't think it is possible. One of the reasons is that what you see is a model and not the database schema. What you don't see is how entity is mapped to the database. In the basic scenario there is 1:1 mapping indeed but once you start customizing your model you may end up having columns in the database with names different from the property names in the model, you may have complex properties you would not even have corresponding properties for on the diagram, you may put multiple entities to one table or one entity to multiple tables. How are you going to show this? And since it is just entity model and not table model data does not seem to be a good fit. I don't know of any tool that could do what you need. Edmx is an Xml file and the designer itself is extensible and based on DSL. You can try cooking something yourself but it will be a lot of work.

Resources