How to update Hibernate applications in production the right way? - java

I read the discussion about using hbm2ddl.auto=update in order to auto-update changes to the database schema.
The thread is from 2008 and I do not know how secure it is to use the auto-update mode today.
We are running a small JavaEE on a Glassfish with Hibernate 4.3.11 and PostgreSQL. We plan to use continious integration with Jenkins.
Is it useful to work with hbm2ddl.auto=update enabled? Or is it better to use an easy alternative to update/check the updates maybe manually?
I know it is hard to give a blanket statement.

You should not use hbm2ddl.auto=update to update production databases.
Few reasons:
Hibernate will only INSERT missing columns and not modify existing columns. Therefore, if you rename a property (Client to Customer), Hibernate will create a new column Customer, leaving the column Client untouched. You will need to manually "move" the data there and remove the orphan column.
Hibernate will not remove constraints on no longer mapped columns. Thus, if your Client column was NOT NULL, any insert query to that table will now fail in the first place, because Hibernate won't provide any data for the orphan column (Which still has it's NOT NULL constraint) anymore.
Hibernate will not touch data types of existing columns. So, if you change a property type from String to Date - Hibernate will leave the column definition as varchar.
Hibernate does not remove columns of which you deleted the property, leading to data-polution and worst-case (The constraints remain in place) to no longer working applications.
If you create additiional constriants on existing columns - hibernate will not create them, because the column already existed before. (You might miss important contraints on the production db you added on existing columns)
So, perform your updates on your own is safer. If you have to take into account what hibernate is doing and what not - you'd better do it on your own from the scratch.

Related

Asynchronous inserts in audit table in spring-hibernate

I have a DB table with many columns and associated Entities.
Update is supported on some of the columns. I need to maintain history of the data that's overwritten in update/delete in a separate table. Options that I have considered are below:
1. Hibernate-envers: Most easiest to use but issue with this is the insert in audit table are synchronous and also it becomes a part of actual transaction. Which is not a desired solution for my use-case.
2. Debezium: While it does make the audit insert asynchronous, but it looks like an overkill for my use-case as it includes installation of a lot of services like Kafka, zookeeper and there seem to be multiple points of failure.
3. JPA listeners: I can use these to get the data being updated/deleted and call an async insert in history table. Only issue I see here is I'll have to replicate actual entity classes code in the history entities.
Please suggest a solution I can go ahead with. Thanks.

Manually Add Audit Entry to Hibernate Envers

I am auditing a Java object using Hibernate Envers annotations, but initial object creation occurs directly in the database using Pentaho (ETL).
I want to create the object using ETL and add a table entry to the Envers generated object_AUD and REVINFO tables.
I have been trying to find the generation strategy for the REV column from the REVINFO table, but I must be looking in the wrong places. Would someone help me find an effective generation strategy so I can manually insert records into the audited tables without causing possible collisions or weird behavior in the future?
What you seek is going to depend on whether or not you are configuring your application to take the default for org.hibernate.envers.use_revision_entity_with_native_id.
The default value (true) tells Envers to ask Hibernate to create the REVINFO table using a native-based primary key which will either be IDENTITY or SEQUENCE depending upon your database platform. If you look at the table definition for REVINFO in your database, you should be able to deduce this information.
If this property is configured using false, Envers will construct its own sequence metadata and provide that to Hibernate. The sequence is called REVISION_NUMBER and is stored in a table called REVISION_GENERATOR. The sequence is initialized to 1 and incremented by 1 as the default.

jooq, fill field value on insert, similar to record_timestamp

Each table in my database have these fixed columns:
record_version
record_timestamp
create_time
Thanks to jooq feature record_version and record_timestamp are automatically handled.
Is it possible to centralize and automate the maintenance of create_time ?
Internally jooq is already managing the first two special fields.
Can I put a handler somewhere to fill the create_time value?
Doing so, I could remove some boilerplate code to initialize this field.
I have another field on each table: update_by_account; anyway If I'm able to manage the previous mentioned field (create_time) I think I'll handle this field too.
thanks in advance
Future jOOQ versions:
What you're looking for is the Listener API feature for Record and UpdatableRecord. It has not yet been implemented as of jOOQ 3.0
In the future, this feature will allow to inject some behaviour to jOOQ's records, when they are stored, inserted, updated, deleted, etc. This would include what you are requesting as well as what jOOQ is currently doing with its
record_version
record_timestamp
columns used for optimistic locking.
A solution for jOOQ 2.x or 3.0:
What you can do right now is implement an ExecuteListener and let it "detect" if it should become active and override a bind value for create_time. This will probably have to resort to parsing or at least regex-matching your SQL statement.
A SQL-only solution:
However, the best option for create_time, in my opinion, is to write a trigger for every one of your tables (Oracle syntax):
CREATE OR REPLACE TRIGGER my_trigger
BEFORE INSERT
ON my_table
REFERENCING NEW AS new
FOR EACH ROW
BEGIN
:new.create_time = SYSDATE;
END t_triggers_trigger;
This will guarantee that the value is available no matter how you access your database...

Managing history records in a database

I have a web project that uses a database to store data that is used to generate tasks that would be processed for remote machines to alter that records and store new data. My problem here is that I have to store all that changes on each table but I don't need all these information. For example, a table A could have 5 fields but I only need 2 for historical purposes. Another table B could have 3 and I would have to add another one (date for example). Also, I don't need changes during daily task generation, only the most recent one.
Which is the best way to maintain a change history? Someone told me that a good idea is having two tables, the A (B) table and another one called A_history (B_history) with the needed fields. This is actually what I'm doing, using triggers to insert into history tables but I don't feel comfortable with this approach. My project uses Spring (Spring-data, Hibernate and JPA) and if I change the DB (currently MySQL) I'd have to migrate triggers. Is there a good way to manage history records? Tables could be generated with Hibernate/JPA annotations.
If I maintain the two tables approach, can I add a method to the repository to fetch rows from current table and history table at once?
For this pourpose there is a special Hibernate Envers project. See official documentation here. Just configure it, annotate necessary properties with #Audited annotation and that's all. No need for DB triggers.
One pitfall: if you want to have a record for each delete operation then you need to use Session.delete(entity) way instead of HQL "delete ...".
EDIT. Also take a look into native auditing support of spring data jpa.
I am not a database expert. What I have seen them do boils down to a few ways of approach.
1) They add a trigger to the transactional table that copies inserts and updates to a history table but not deletes. This means any queries that need to include history can be done from the history table since all the current info is there too.
a) They can tag each entry in the history table with time and date and
keep track of all the states of the original records.
b) They can only
keep track of the current state of the original record and then it
settles when the original is deleted.
2) They have a periodic task that goes around and copies data marked as deletable into the history table. It then deletes the data from the transactional table. Any queries in the transactional table have to make sure to ignore the deletable rows. Any queries that need history have to search both tables and merge the results.
3) If the volume of data isn't too large, they just leave everything in one table and mark some entries as historical. Queries have to ignore historical rows. Queries that include history are easy. This may slow down database access as the table grows to include many unused rows but that can sometimes be ameliorated by clever use of indexes.

Workaround for Spring/Hibernate due to non-standard behaviour of UNIQUE constraint in MS SQL

There is a UNIQUE database constraint on an index which doesn't allow more than one record having identical columns.
There is a piece of code, managed by Hibernate (v2.1.8), doing two DAO
getHibernateTemplate().save( theObject )
calls which results two records entered into the table mentioned above.
If this code is executed without transactions, it results INSERT, UPDATE, then another INSERT and another UPDATE SQL statements and works fine. Apparently, the sequence is to insert the record containing DB NULL first, and then update it with the proper data.
If this code is executed under Spring (v2.0.5) wrapped in a single Spring transaction, it results two INSERTS, followed by immediate exception due to UNIQUE constraint mentioned above.
This problem only manifests itself on MS SQL due to its incompatibility with ANSI SQL. It works fine on MySQL and Oracle. Unfortunately, our solution is cross-platform and must support all databases.
Having this stack of technologies, what would be your preferred workaround for given problem?
You could try flushing the hibernate session in between the two saves. This may force Hibernate to perform the first update before the second insert.
Also, when you say that hibernate is inserting NULL with the insert, do you mean every column is NULL, or just the ID column?
I have no experience in Hibernate, so I don't know if you are free to change the DB at your will or if Hibernate requires a specific DB structure you cannot change.
If you can make changes then you can use this workaround in MSSQL tu emulate the ANSI behaviour :
drop the unique index/constraint
define a calc field like this:
alter table MyTable Add MyCalcField as
case when MyUniqueField is NULL
then cast(Myprimarykey as MyUniqueFieldType)
else MyUniqueField end
add the unique constraint on this new field you created.
Naturally this applies if MyUniqueField is not the primary key! :)
You can find more details in this article at databasejournal.com

Categories

Resources