Hibernate Envers #GeneratedValue skips values - java

I am working on a project with Hibernate and its Envers jar. I am also using Oracle 11g. It works fine except for one thing. When I give a #Id and #GeneratedValue to an #Audited entity say User and I insert new lines in my USERS table the id skips a value ( the ids should be like 3,4,5,6... but are 3,5,7,... ) and the REV column in the USERS_AUD table also skips sometimes. Probably, that's bacause the ID and REV fields in the USERS_AUD table cannot be equal ( an entity in that table cannot have the same id and revision number ). How can I modify this behavior so that the ids in my USERS table are generating normally ?

Related

JPA mapping with two ManyToOne using an intermediary table (middle entity)

I am trying to set a JPA mapping with JoinTable, and it seems to be ignored when Hibernate (my JPA implementation) is doing a query.
To explain the use case
Each time a user gets a page of my app, I insert a line in the USAGE_LOG table (with the id of the user and the id of the page).
Each page is related to a category (for instance: settings, orders, items, news...) and a type (for instance create, update, display, delete).
So, I have some kind of middle entity table, that links a page to: a category + a type. Like a triplet: (page, category, type)
My table structure
table USAGE_LOG (for information only, this one works well)
ID PrimaryKey
USER_ID Foreign key to column ID of table USER
USAGE_LOG_PAGE_ID Foreign key to column ID of table USER_LOG_PAGE
table USAGE_LOG_PAGE
ID PrimaryKey
URL VARCHAR
USER_ACTION_ID Foreign key to column ID of table USER_ACTION
table USER_ACTION
ID PrimaryKey
ACTION_CATEGORY_ID Foreign key to column ID of table ACTION_CATEGORY
ACTION_TYPE_ID Foreign key to column ID of table ACTION_CATEGORY
table ACTION_CATEGORY
ID PrimaryKey
NAME VARCHAR
table ACTION_TYPE
ID PrimaryKey
NAME VARCHAR
So the USER_ACTION table is a join table with the particularity that it links a USAGE_LOG_PAGE to a ACTION_CATEGORY and a ACTION_TYPE at the same time.
Also, I can have several USAGE_LOG_PAGE that are linked to the same ACTION_CATEGORY and ACTION_TYPE.
Unfortunately, I cannot change the database structure (it is legacy code).
I have tried the following Mappping on the Entity "UsageLogPage"
#ManyToOne
#JoinTable(name="action",
joinColumns=#JoinColumn(name="ID", referencedColumnName="USER_ACTION_ID"),
inverseJoinColumns=#JoinColumn(name="ACTION_CATEGORY_ID", referencedColumnName="ID"))
#Getter #Setter
private ActionCategory actionCategory;
#ManyToOne
#JoinTable(name="action",
joinColumns=#JoinColumn(name="ID", referencedColumnName="USER_ACTION_ID"),
inverseJoinColumns=#JoinColumn(name="ACTION_TYPE_ID", referencedColumnName="ID"))
#Getter #Setter
private ActionType actionType;
(I use Lombok for #Getter and #Setter)
This mapping compiles, but when I try to get data, I have the following exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'usagelogpa0_.actionCategory' in 'field list'
Indeed, the Hibernate query is:
select usagelogpa0_.ID as ID1_80_0_,
usagelogpa0_.actionCategory as actionCa2_80_0_,
usagelogpa0_.actionType as actionTy3_80_0_,
usagelogpa0_.URL as URL5_80_0_
from usage_log_page usagelogpa0_
where usagelogpa0_.ID=?
(the key part is the "actionCategory" and "actionType" in the select)
This is not what I expect, Hibernate should do a join.
Have you any idea of what I did wrong?
Thanks !
After lots of investigations, I have found that:
it wasn't working as expected because I put the #ManyToOne and the #JoinTable annotations at the attribute level. I created a getter by hand and put the annotations on it, and they were taken into account
it still wasn't working correctly, because Hibernate didn't find the column "USER_ACTION_ID" on the USAGE_LOG_PAGE table, at run time. This column wasn't in the available fields, for a reason (that I coudn't find). When adding a field "usage_action_id" in the entity "UsageLogPage", it found the attribute, but refused to create the mapping because USAGE_ACTION_ID isn't a primary key.
At the end, even if I couldn't change the database, I could change the object model.
So I created the middle entity "UserAction", binded it with ManyToOne on the UsageLogPage entity, removed the attribute "actionCategory" and "actionType" from the UsageLogPage and added them as ManyToOne in the new UserAction entity.
If you have a table that acts as a middle entity for 2 different ManyToOne relationships, perhaps the best solution is to create the middle entity in your object model.

Hibernate generates negative id after a certain amount of insert

I have a weird behavior of Hibernate (3.6.1.Final) in my application and I am pretty desperate at the moment. The behavior occurs on both MariaDB 10.1 and RDS on Amazon.
After a certain amount of successful persists (always around ~5k) Hibernate fires a:
SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails
From what I see in the stacktrace Hibernate tries to fill a foreign key in a entity with a negative value that violates the FK constraint (the target table of the FK does not have such primary key value).
The strange thing is:
It happens only after around ~5k successful persists
After submitting such number of persist operation, the error occurs even if I restart the application (as well as the DB). Only solution is to wipe out the DB and repeat.
The error occurs independently from the load (continuous, in batches, single or multithread).
The following is the FK of the entity that is violated during the insert:
#Entity
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Charge extends Entry {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "totalId")
private Amount total;
//....
And this is how the primary key is defined in the Amount entity:
#GeneratedValue
#Id
#Column(nullable = false)
private Integer pk;
//....
My questions are:
Why a non-valid negative value occurs in the FK ? (the primary key targeted by the FK is set as auto increment, starting from one and all the preceeding inserts worked perfectly accordingly to this)
Why does the error occurs only after ~5k persists ?
Why not even a restart of the application fix the problem? DB is fine because a manual insert outside the application works (with valid FK values)
My suspicion is Hibernate and how it manages the auto_increment on the DB. The are many posts on the topic but none of them fit my specific case (error occurring only after a certain usage).
Last note: On the DB auto_increment is initially set to 1. All the initial persists (before the error appears) of the Amount entity have a incremental PK that start by 1: (1,2,3, ...). So again why after a while Hibernate comes up with a negative (incompatible to the FK)?
Thank you so much in advance for your help.
Best
G.
Maybe is the limite of type int ?
Try this :
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
#Column (name = "id")
private long id;
we had exact the same issue and it was due to old mariadb jdbc driver. We were using version 1.4.2
After switching to the version 1.5.5 of mariadb jdbc driver the problem were gone

JPA/MySql An eniity with incremental primary and secondary id

I have a resource ,say a Book.
I want the book to have a book id and version id
On Create operations i want to have incremental id
book id version id status
1 0 ACTIVE
2 0 ACTIVE
3 0 ACTIVE
On update i want to have a new version for same id
book id version id status
1 0 INACTIVE //Changed to inactive
1 1 ACTIVE //new row with same id
2 0 ACTIVE
3 0 ACTIVE
i have two tables to achive this
A table to generate the id
CREATE TABLE `BookIdGenerator` (
`id`int(11) NOT NULL AUTO_INCREMENT ,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
The Book table
CREATE TABLE `Book` (
`id` int(11) NOT NULL ,
`version` int(11) NOT NULL,
`title` varchar(255) NOT NULL,
`isbn` varchar(255) NOT NULL,
PRIMARY KEY (`id`,`version`),
foreign key (`id`) references BookIdGenerator(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The entity class is
class Book{
private long id;
private int version;
//other fields
What would be the correct table structure and JPA annotations to achive this?
Without having any separate table I can have #Id at id and version but this does not allow to retrieve the assigned id to an object on persisting it.
update : I can have an Emeddable BookId class(with book id and version) and can use an #EmbeddedId in Book but I have to write a Id generator to generate the id.
What can I do so that i don't have to write a generator to assign id before persisting in create method ?
JPA #Version
Your intended use of the version column is not as intended in JPA.
A version field is used to support the Optimistic Locking of individual DB records. Each time a transaction attempts to update a record, the version field is compared with the value in the DB. If they are the same then no other transaction has updated the record. The record will be updated and the version column changed. If they are not the same then some other transaction has updated the record and an OptimisticLockException will be thrown.
When updating entities (with JPA's merge()), JPA updates the existing record.
In your question, when you ‘update’ the book record, you are in fact creating(persisting) a new record.
Further, the version field is managed by JPA and should not be touched by the application – read only. In your example you are creating a new record with a specified version number which is not allowed.
Primary Keys
You have a PrimaryKey defined for Book, however, you require multiple records to hold the same PrimaryKey. This will fail as the PrimaryKey must be unique.
In this case your id and version form a Compound Primary Key, and you’ll need to separate these fields out into a separate class; a Primary Key Class.
You would require an application specific versioning if you really want to record each change to a record as a separate record, as you can't use the JPA version field for your purposes.
Book_id Book_Update_id status JPA_Version
1 0 INACTIVE 0
1 1 ACTIVE 0
2 0 ACTIVE 0
3 0 ACTIVE 0
I’ve left a JPA version column in although this is not required if you are not modifying records.
so your Book will look something like this.
#Entity //Tell JPA this is an entity to be mapped to your DB.
#IdClass(BookId.class) //you need to repeat the PK fields in this class
class Book{
#Id #Column(name=”Book_id”) //Both id and bookUpdateId annotated with #ID
private long id; //Together they for a compound Primary Key
#Id #Column(name=”Book_Update_id”)
String bookUpdateId
#Version #Column(name=”JPA_Version”) //Tell JPA where to record its version info
private int version;
It will be upto the application, when creating new records, to decide what the id and bookUpdateId values are to be based on the existing values of a record. There is no way JPA can know this. There is therefore no separate table required to support ID generation.
See Pro JPA 2 by Mike Keith and Merrick Schincariol for all these topics, a good introduction and beyond. This will aslo explain the #IdClass in more detail.

Why is Hibernate using sequences

I have parent and child entities with IDs that are generated using the strategy GenerationType.TABLE because I'm working with a MySQL database.
If I create the parent without specifying an ID (i.e., create the parent for the first time), add new children to it, and save the parent, then hibernate works as expected and uses the MySQL AUTO_INCREMENT column feature.
However, If I create a parent and specify an ID (i.e., instantiating a parent that has already been persisted), add a child to it, and save the child, then hibernate issues a select sequence_next_hi_value from hibernate_sequences ... and uses that as the child's PK.
Likewise, if I instantiate a parent by getting it from the database via session.get(Parent, 1), add a new child to it, and save either the parent or the child, then hibernate uses sequences to get the child's PK.
If I create enough new parents (32767, to be exact) to run up the mysql AUTO_INCREMENT counter, there would be failures due to the primary key not being unique enough.
Here are my parent and child entities (named Location and Category, respectively):
#Entity(name="location")
public class Location {
#Id #GeneratedValue(strategy=GenerationType.TABLE) #Column(name="location_id")
private int id;
#OneToMany(mappedBy="location")
#Cascade(CascadeType.SAVE_UPDATE)
private List<Category> categories;
...
}
#Entity(name="category")
public class Category {
#ID #GeneratedValue(strategy=GenerationType.TABLE) #Column(name="category_id")
private int id;
#ManyToOne #JoinColumn(name="location_id")
private Location location;
}
Here is the hibernate code:
Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'category' for update
Hibernate: update hibernate_sequences set sequence_next_hi_value = ? where sequence_next_hi_value = ? and sequence_name = 'category'
Hibernate: insert into category (location_id, category_id) values (?, ?)
Beforehand: I have no answer why hibernate uses another strategy when persisting your child record. But I may give you some answers or hints about the other issues you described:
Regarding the strategy GenerationType.TABLE, your application relies on the database functionality of generating an ID. This may cause database round-trips for each new database record inserted. When using an alternative strategy like hi-lo, the generator will reserve a bunch of ids (for example 100 or 1000) by contacting the database only once. This may improve performance greatly. On the other side, you may loose some ID when the application is restarted. If you may life with this issue hi-lo should be considered.
Another benefit of using a strategy not relying on a database product is, that if you switch databases you may keep your mappings.

Hibernate returns only one result (After changing table names)

I am using hibernate retrieve results from my MySQL database into my Java project. Recently, I had a lot of redundant data and had to manually clean up the database by copying the required data into new tables and then renaming the newly created table to old table.
But, now querying the database with hibernate gives only one row as the result. I have manually checked the database and there are several different rows in the database. My query to Hibernate is something like this:
Criteria c = session.createCriteria(UserDto.class);
c.setMaxResults(100);
List<UserDto> users = c.list();
users contains 100 elements but all are the same.
The mapping of userDto is here.
Any idea what is happening here?
If your UserDto class has ToMany relations, then this is quite possible that outer join on them results in many records which all contain one and the same user data. You should use
session.createCriteria(UserDto.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
Double-check your mapping of the UserDto class to the database.
My guess is that you don't have it mapped to the table that you think you do.
As Sebastien mentioned, setting hibernate.show.sql to true should make this obvious.
Did you deleted the old tables? And in the configuration file what is the value for "hibernate.hbm2ddl.auto"?
I think the reason is these records have same id, so Hibernate treat them as the same record. You can check it.
I had same problem. In my case, the problem detected when I created a table in MySQL manually and I tried to read data from that table using hibernate and a dto class. After checking my dto class fields and database table, I figured out that there is a difference between table column named "id" and the class field which named dbId. The code was something like this:
#Id
#GeneratedValue
#Column(name="db_id", unique = true)
private long dbId;
So I edited the name and changed the code:
#Id
#GeneratedValue
#Column(name="id", unique = true)
private long dbId;
Which "id" was the correct name of databse table column and the problem has been solved.

Categories

Resources