How does Envers deal with schema changes? - java

I am thinking about switching from a self-implemented versioning-solution to Hibernate Envers, but I am not quite sure yet. I have read a lot about it, but I am concerned about schema changes and how Envers deals with them after having historized data according to an older schema.
What is your experience with Envers in this regard? How do you deal with schema changes and existing data with Envers?
Update 1:
It is not just about adding removing simple columns from a table, but e.g. when changing a simple Forein-Key-Relationship into a separate entity with two 1:n-relationships (M2M with attributed columns. This is a "logical" change in your data model. How do you deal with that when using Envers, when there is already historized data according to the old model? Is there an alternative to manually write sql-scripts and transfering them into the new representation?

In my experience, Envers simply copies every field from your entity table to its audit tables. The copied fields in the audit tables have no constraints on them, including nullability and foreign key constraints, so there's no problem with adding or removing such constraints on the real tables. Any kind of relationships you add to your entities will just be new audit columns and/or tables added under Envers, and it's up to you to correctly interpret them in their historical context.
For your example, if I understand correctly, of switching from a join-column-based relationship to a join-table-based one, you'd simply have the old join column coexisting with the join table, and at the point of the cutover, the former will cease being populated in favor of the latter. Your history will be completely preserved, including the fact that you made this switch. If you want all the old data to fit into the new model in the audit tables, it's up to you to do the migration.

There shouldn't be problems with modifying the existing schema as Envers relies on your #Entities to create the audit tables. So if you add or remove a column from an existing table, as long as this change is reflected in your #Entity / #Audited JavaBean, it should be ok.

The foreign key refactoring should be fine with Envers. As Envers creates a join table even for one-to-many relationship, it should be straight to change it to become many-to-many relationship. I extracted one paragraph from official document:
9.3. #OneToMany+#JoinColumn
When a collection is mapped using these two annotations, Hibernate
doesn't generate a join table. Envers, however, has to do this, so
that when you read the revisions in which the related entity has
changed, you don't get false results.
To be able to name the additional join table, there is a special
annotation: #AuditJoinTable, which has similar semantics to JPA's
#JoinTable.
One special case are relations mapped with #OneToMany+#JoinColumn on
the one side, and #ManyToOne+#JoinColumn(insertable=false,
updatable=false) on the many side. Such relations are in fact
bidirectional, but the owning side is the collection (see alse here).
To properly audit such relations with Envers, you can use the
#AuditMappedBy annotation. It enables you to specify the reverse
property (using the mappedBy element). In case of indexed collections,
the index column must also be mapped in the referenced entity (using
#Column(insertable=false, updatable=false), and specified using
positionMappedBy. This annotation will affect only the way Envers
works. Please note that the annotation is experimental and may change
in the future.

Related

Why we should use Spring Data JPA mappings?

This can seem a stupid questions for some people, but I couldn't find any information anywhere why we should use mappings (#OneToOne, #OneToMany etc) in JPA while defining entity classes. I know one the advantage is code reduction, so that we don't have to explicitly write queries in order to fetch data from relationship tables. But is there any other benefit (from code optimisation perspective at SQL side) that we have?
The reason is to load object trees or graphs.
That's the goal of object-relational mapping to fill the gap between database tables and objects.
And as you said it reduces code.
In a summary the idea of the ORM is to map tables to objects, so the developer works with the objects instead of the tables.
Tables in SQL have relationships through key columns.
in JPA these relationships are expressed via #OneToMany, #OneToOne etc.
This means that if you want to fetch a row from one table and join that with corresponding row from another table (via a relationship) the JPA implementation (mostly Hibernate) can do that for you, looking at the relationships you have defined for your entities.
You need to describe the entities relationships because it's part of the DB schema model which you are mapping to application level objects. As you mention - it saves you writing the SQL queries yourself, but that's not the main point.
The main point is that you can model/represent one domain (database tables, rows, relationships, SQL commands) as another type of domain (objects/classes, OOP paradigm, programming language commands) which completely shifts the way you work with it.

Get PersistentSet from EntityManager or Hibernate Session

Hibernate returns org.hibernate.collection.internal.PersistentSet as Set implementation on #OneToMany relation:
#OneToMany(mappedBy = "group", cascade = CascadeType.PERSIST)
private Set<Student> studentSet;
Hibernate tracks all changes on PersitanceSet (if some entity will is added to Set than it will be inserted into a database and etc.). Is it possible to have the same functionality for collections got by JPA EntityManager, org.hibernate.Session or by another way?
For example:
entityManager.createQuery(query, Student.class)
.setParameter("name", name)
.getResultList();
Doesn't return such kind of collection.
So I am searching the way to get elements by custom query and collect elements into a collection that Hibernates tracks all changes(inserting on adding new transient entities, updating on changing managed entities, deleting on removing from the collection)
What you're asking for is not possible in Hibernate.
Hibernate tracks all changes on PersitanceSet (if some entity will is added to Set than it will be inserted into a database and etc.)
That statement is not really accurate. Hibernate will not automatically insert an entity added to the set into a database. You need to opt in for that functionality specifically by declaring the appropriate cascading option (CascadeType.PERSIST in this case).
What Hibernate will do, however, is track associations between entities. If a collection represents the owning side of a to-many association, changes to the collection will establish/destroy associations between entities. In fact, Hibernate will track all other entity state, not just associations. That's the idea behind managed entities - to be able to work with a domain object just like with any other Java object, and let Hibernate take care about persistence behind the scenes.
A collection retrieved from a query does not represent part of a single entity state. Therefore, there would be little sense for Hibernate to track the structural state of the list. Suppose you made two queries for the same data within a single transaction. You then modify one of the result lists and leave the other intact. What do you think should happen in such a scenario?
Note that by 'not possible', I mean to say that Hibernate does not provide such a functionality out of the box. However, if you want to track changes to an arbitrary list, there are list implementations that allow that (see e.g. Glazed Lists or Apache Commons Events). You could combine them with Hibernate API to get the behavior you want.

Linking entities to existing tables without code

Let's assume that we have java entities already implemented and annotated with Jpa annotations.
We also have an existing database slightly different to the schema described by said entities.
How I can link the data base with my entities without the code?
Otherwise, how can i proceed from the begining when implementing my entities to make this stuff configurable ( give the user the possiblity of specifying the names of the columns corresponding to the fields of each entity in an externalized configuration file)?
NB: I use hibernate as an ORM.
I believe this is what you are looking for

JPA - Redirect one entity Update/Delete statements to a mirror table

I am working with Java EE 7 on a Wildfly server. I have a strange scenario, where the client has two tables - "employees" and "employees_modified". The second table has the exactly same structure as the first one and servers as a modification storage. So if an employee changes his name from "john" to "john-1", we will write to employees_modified
insert into employees_modified(first_name) values("john")
Please note that the other fields in the table "employees_modified" are empty.
The question is: is there a way to somehow map the two tables and overwrite the values from employees by those in employees_modified where they are present.
I looked at #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) and #AttributeOverrides but those solutions don't seem to fit to my scenario.
Look at hibernate envers, it solves your problem simply. Attach envers to your project. Place the first table under audit with annotation #Audited and #AuditTable(value = "employees_modified"). But as pointed by #Predrag Maric it is important to leave other fields of the second table empty, you can use #PostPersist (or listener in pure hibernate) method in entity. In this method you can describe additional logic employees_modified entity creation and persisting.
You can use #SQLUpdate and #SQLDelete to customize the CRUD statements to be redirected to a different table:
#Entity
#SQLUpdate( sql="UPDATE employees_modified SET name = ? WHERE id = ?")
#SQLDelete( sql="DELETE FROM employees_modified WHERE id = ?")
public class Employees {
...
}
If it's only for auditing, I agree with the answers before me (triggers, events etc).
If you actually want to access that "employees_modified" table (e.g. run complex Hibernate queries) than you can use a second persistent unit. The following post: https://developer.jboss.org/thread/237078 seems to indicate that's a recommendation from hibernate. Obviously the 2nd unit will need xml configuration rather than annotation - at least it can't rely on the same #Table annotation.
BTW there's also some documentation about a #SecondaryTable annotation, but it's my understanding that it doesn't match your case (because your business probably needs to treat those tables differently - sometimes you want to view just the history, sometimes just the live data)/

Hibernate Annotation Confusion

Recently, I have been learning about Hibernate, and I am facing some difficulties. My first problem is as follows: I am very much confused with the below terms.
Bidirectional mapping
Many to One
Because, as far as I know, in rdbms we first need to insert in parent table. Then we can insert on child table, so the only possible scenario is one-to-many (first parent then children). Then, how is many-to-one is going to work? Second, what is this bidirectional mapping in regards to Hibernate. Specifically, different types of join annotations confuse me a lot. I am listing those annotations below.
1.#JoinTable(name = "Tbale_Name", joinColumns = { #JoinColumn(name = "Column_Name") },
inverseJoinColumns = { #JoinColumn(name = "Another_ColumnName") })
2.#OneToMany(mappedBy="department")` this mappedby term
3.#PrimaryKeyJoinColumn
Please help me understand these concepts.
The first thing I would say is don't think in terms of tables but think in terms of Objects.
What you are trying to express with the annotations is the relationship between objects, let hibernate work out how to persist the data. You can obviously manually check the SQL but the idea of using an ORM is to map the relationships between entities accordingly and let the ORM figure out the complexity around generating SQL etc.
Its worth noting that the parent -> child relation can be mapped using #ManyToOne by adding mappedBy to the non-owning (child) side of the relationship. Hibernate then will determine which entities to insert into the database first. Running with a TransactionManager will enforce integrity with multi table inserts. Hibernate will also workout which entities need to be persisted, for example if you add an new object on the many side to an existing object on the one side.
Furthermore, its worth understanding that in some cases it won't always be the database that generates the primary key in a parent -> child foreign key. Its possible for the code to generate the Identifier and hibernate will persist them according.
Bidirectional mapping means that object entities have a reference to each other. i.e. You can retrieve the second entity from the first entity. Bidirectional mapping supports one-to-many
or many-to-many. I.e. OneToMany = a Set on one of the entities. Many-To-Many = Sets on both entities.
JoinTable tells hibernate that a table in the database can be used to map to other tables together. See JPA "#JoinTable" annotation for more information. JoinColumn tells hibernate what column to use to make the join between the two entities. Hibernate needs these to construct the SQL.

Categories