I have a JPA entity which I want audited. I added Envers to my project, and added the #Audited annotation to the entities I need. Now, the changes are being logged in the audit tables, but I cannot retrieve them through the audit readers provided by Envers.
My entity is as follows.
#Entity
#Audited
#AuditTable(value = "blog_posts_AUD")
#Table(name = "blog_posts")
public class Post {
...
}
I'm trying to query the audit tables as follows.
AuditReader reader = AuditReaderFactory.get(entityManager);
List revisions = reader.getRevisions(Post.class, primaryKey);
This fails, because the SQL call contains a reference to a table called org.foo.bar.blog_posts_AUD, which obviously does not exist. It seems that Hibernate is not picking up the #AuditTable annotation (or the default audit table suffix, for that matter). Anyone ever faced this before?
Related
I hope someone have experienced something similar and can help me:
I am using graphql-java (and spring, graphql-java-tools etc.) and hibernate and I am experiencing a weird issue:
Whenever i execute a query (or mutation) and load an entity via Hibernate, it automatically lazy loads the relations. I can see this when looking in Hibernates query log.
This happens even though i dont load the field in the query, and even also when i delete the field from the schema altogether.
Example, given the following schema:
query {
getAllItems: [Item!]!
}
Item {
id: String!
name: String!
owner: Person!
}
Person {
id: String!
name: String!
items: [Item!]!
}
And a Hibernate entity (pseudo code):
#Entity
class Item {
#Id
private String id
#Column
private String name
#ManyToOne(fetch = FetchType.LAZY)
private: Person
...
}
The following query:
getAllItems {
id
name
}
And a hibernate query that loads just the items, would end up with first fetching all the Items in one query, and then fetching all the owners in a seperate query each (unless a owner is the same in multiple items, then its returned from the hibernate cache).
So my thought was that graphql-java recursively scans the objects that is returned to it, which causes the hibernate proxies to fetch.
Can i be right about this, or do you think my issue is completely unrelated to graphql-java?
UPDATE:
I found out that this has nothing to do with graphql, and is caused by hibernate. My relations are setup as LAZY, but Hibernate ignores this, and makes a query for each Person. So first a query that gets all Item's and next a query for each Person (n+1). And i do not access the proxies myself.
I create the query like this (this is kotlin):
entityManager
.createQuery("SELECT i FROM Item i", Item::class.java)
.setMaxResults(1000)
.resultList
To make things clear, this has nothing to do with GraphQL.
Hibernate eagerly loads one-to-one relationships by default.
To change this behaviour annotate the person field with
#OneToOne(fetch = FetchType.LAZY)
I'm currently using Hibernate & Envers version 5.2.9.Final. I want to use #ElementCollection with a custom table name for both the collection and the audit table.
What I know so far is that modifying default table names has a variety of annotations to work with: For the entity itself there are the annotations #Table and #SecondaryTable as well as the corresponding envers annotations #AuditTable and #SecondaryAuditTable. For changing the table name of an element collection there is the #CollectionTable annotaion. I have not been able to find a corresponding envers annotation so far. So my question is:
How can I change the name for a hibernate #ElementCollection envers audit table?
Additional info
In the hibernate envers ticket which tracks the adding of auditing support for element collections, the same question was asked back in 2013 but not answered.
A code snippet to make my setup clear:
#Entity
#Table(name = "\"user\"")
#SecondaryTable(name = "\"user_secondary\"")
#Audited
#AuditTable("\"user_audit\"")
#SecondaryAuditTable(secondaryTableName = "user_secondary",
secondaryAuditTableName = "\"user_secondary_audit\"")
public class User {
// ... stuff like id and other fields ...
#ElementCollection
#CollectionTable(name = "\"user_references\"")
private Map<String, Long> references = new HashMap<>();
// TODO FIXME how to get a custom name for the audit table?
// ... more stuff like getters and setters
}
Hibernate generates all tables as intended, yet the collecction audit table is named 'user_references_AUD' while I would like to get the name 'user_references_audit' like for the other tables.
I'm also aware of the global settings affecting the audit table prefix or suffix, but that is only a last resort for my use case.
Update
As suggested I added a feature request to Hibernate JIRA.
That is because Envers has no complement for #CollectionTable.
You are welcomed to add a JIRA requesting that we add a complementing annotation and I can look at what is needed to add the functionality. Just at a glance, it shouldn't require too much as it merely needs to feed into the generated Envers entity table name for the collection middle entity.
I have 2 Entitites, one maps to a database table, the other to a database view.
The data of the view depends on the table.
#javax.persistence.Table(name = "BOOKING_INFO", schema = "BOOKING")
#Entity
public class BookingInfo extends AbstractBooking {
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
public class BookingView extends AbstractBooking {
This works fine in most cases, however when we write (insert or update) the Booking entity and then do queries (in my case a count) on the BookingView entity, we get stale data.
Why this happens is clear to me (hibernate caching, it only flushes when it detects that a select needs some data flushed).
So if I would do a query on the Booking entity, it would trigger a flush.
I have found the #Synchronize Annotation in Hibernate which sounds like it should fix this problem, like this:
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
#Synchronize("BOOKING.BOOKING_INFO")
public class BookingView extends AbstractBooking {
However this does not make any difference (flush only happens at the end of the transaction). Also the documentation I have found about this annotation is quite lacking and not very helpful.
EDIT: I also tried #Synchronize("BOOKING_INFO") (without the schema name, and also lowercase, but that made no difference)
The docs say that it is mostly used with #Subselect but it is not a must (I don't want that).
Has anyone ever successfully used this annotation?
Is there any other way to handle database views in Hibernate?
Am I missing something else?
Thanks to a colleague we were able to debug and fix this, the problem was that our Hibernate naming-strategy lowercased our table-names, so the correct annotaiton is:
#Synchronize("BOOKING.booking_info")
How to debug this:
set breakpoints in Hibernates ActionQueue class in the areTablesToBeUpdated methods.
There we saw that it compared "BOOKING.BOOKING_VIEW" to "BOOKING.booking_view".
We think this is a bug in hibernate because it should either apply the naming-strategies also to the values from #Synchronize or compare these case-insensitive (which could theoretically lead to too many flushes if you have a crazy database which uses tables with the same name only differentiated by casing).
Created a Hibernate issue: https://hibernate.atlassian.net/browse/HHH-10002
In a spring mvc app using hibernate, jpa, and MySQL, I have a BaseEntity that contains an id field that is unique across all classes that inherit from BaseEntity, using #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS). Some data is imported into the MySQL database using an external dml.sql file run from the command line. The imported data is carefully planned so that all the ids that need to be managed as part of the BaseEntity inheritance group are unique within their inheritance group.
The problem is that hibernate is not taking the values of the ids already in the database into account when it inserts a new record into the database. Instead, hibernate is saving an id value in one of the descendent entities which is identical to an id stored in one of the other descendent entities.
How can I configure hibernate to respect the id values already in the database when it saves a new entity within the same inheritance group?
Some relevant facts are:
All of the objects in the MySQL database were created directly from the hibernate mappings in the app by using hbm2ddl.
I cannot use #MappedSuperClass for BaseEntity because BaseEntity is used as a property of one of the entities in the app, so that entities of various types can be stored in the same property of that entity. When I was using #MappedSuperClass, eclipse was giving compile errors saying that BaseEntity cannot be instantiated directly because it has #MappedSuperClass annotation.
Note: The file sharing site seems to be center-justifying all the code. You can fix this by simply cutting and pasting it into a text editor.
You can read the code for BaseEntity by clicking on this link.
The code for the entity whose id values are being set incorrectly by hibernate can be read by clicking on this link.
The jpql code for saving the entity whose id is being set incorrectly is as follows:
#Override
#Transactional
public void saveCCD(HL7ConsolidatedCareDocument ccd) {
if (ccd.getId() == null) {
this.em.persist(ccd);
this.em.flush();
}
else {
this.em.merge(ccd);
this.em.flush();
}
}
I have never done this using hibernate or mysql ut have done something similar with EclipseLink + PostgreSQL. So there might be some mistakes below.
With generation type TABLE you might want to explicitly specify some additional parameters using the TableGenerator annotation. That way you are certain where hibernate is storing things.
#Id
#GeneratedValue(
strategy=GenerationType.TABLE,
generator="TBL_GEN")
#javax.persistence.TableGenerator(
name="TBL_GEN",
table="GENERATOR_TABLE",
pkColumnName = "mykey",
valueColumnName = "hi"
pkColumnValue="BaseEntity_Id",
allocationSize=20
)
What you need to do when you bypass hibernate is to reserve the ids you need by updating the row with mykey BaseEntity_Id in the table GENERATOR_TABLE.
For details on the annotations see paragraph 5.1.2.2
I want to use hibernate objects in project as defined below.
#Table(name = "Parent")
class Parent{
int id;
String name;
}
#Table(name = "Child")
class Child extends Parent{
String schoolNo;
}
But in the database;
There is no relation with these two table.
Parent tables columns are; id, name
Child tables columns are; id, name and schoolNo
If I use
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
when I send a query for Parent object, hibernate use UNION on Child and Parent tables but I want to select from only Parent table.
And if I use
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
hibernate wants a discriminator column.
I need hibernate sends select query for each class to its table.
Best regards.
TABLE_PER_CLASS is the correct strategy here.
It's odd that Hibernate generates a union query over both tables, but that should still work. The subquery over the wrong table won't find anything, so the results will be correct. This sounds like a bug in Hibernate's query generation for subclasses.
In a similar situation, I use #Inheritance(strategy = InheritanceType.JOINED) on the parent table.
See more info in the Hibernate docs: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#d0e1168