Is there a way to tell Hibernate to first check if the current primary key generated by a Table Generator is usable or outdated?
I have an application which uses hibernate to create new entries in several tables in my database, but sometimes these generated values are outdated and already used. This happens because this database is used by quite a few applications and scripts, and some of these use the "select MAX(ID)+1"-Keygeneration"strategy". It is not really an option to change all other components to use the table generator (although it would solve the problem), so I have to make sure that the values I get from the table generator are really usable.
Is there any way to tell Hibernate to check the validity of the generated values before it tries to insert a new record into the database (and throw a ConstraintViolationException)?
Or, alternatively, is there a way to manually update the generator tables before hibernate uses them to generate new Ids?
The obvious way would be to run a native query like UPDATE pk_generator SET value=(SELECT MAX(ID)+1 from members) WHERE column='members'
When you save a object with saveOrUpdate() the objects id field will get updated with the auto generated id if it was a create operation. So that it will never conflict with id which was already generated and used.
Related
I'm not sure exactly where the error is coming from, unfortunately, but I have a guess and would like to know the best way to solve it.
Problem
Suppose we have the following table in the database
ID
Field A
Field B
Field C
1
A
C
Something
2
B
C
Something else
And we have two unique indexes on the table
Unique-Index1 (ID)
Unique-Index2 (FieldA, FieldB)
Now I am loading both entities
Session session = ...();
Transaction tx = session.beginTransaction();
TestTable dataset1 = (TestTable) session.get(TestTable.class, 1);
TestTable dataset2 = (TestTable) session.get(TestTable.class, 2);
And now I want to do something like this
update testtable set fielda = 'B' where id = 1;
update testtable set fielda = 'A' where id = 2;
So at the end the unique key is not violated, but after the first statement, the unique index is violated.
In my JAVA application it looks like this
dataset1.setFieldA("B");
dataset2.setFieldA("A");
session.saveOrUpdate(dataset1);
session.saveOrUpdate(dataset2);
tx.commit();
After executing the application I get the following exception
Could not execute JDBC batch update
Unfortunately, the error is not really meaningful. Also, I don't get any information whether it might be a duplicate or not. But if I delete the unique index, it works. So my guess is that it is because of that.
Used frameworks / systems
Java 17 SE application, using Hibernate 3.2 (very old version) with the legacy mapping XML files (so still without annotations). The database is an IBM Informix database.
The database model, as well as the indexes are not generated by Java, but by regular SQL scripts.
I can't change anything about the versions of Hibernate or the database either, unfortunately. Also I cannot influence how the index was created. This all happens outside the application.
Idea
The only idea I had was to first change all records that need to be changed to fictitious values and then set the correct values again. But that would mean that two update statements are triggered per record, right?
Something like this:
dataset1.setFieldA("XXX");
dataset2.setFieldA("YYY");
session.saveOrUpdate(dataset1);
session.saveOrUpdate(dataset2);
dataset1.setFieldA("B");
dataset2.setFieldA("A");
session.saveOrUpdate(dataset1);
session.saveOrUpdate(dataset2);
tx.commit();
However, I am not even sure if I need to commit the transaction. Maybe a flush or something similar is enough, but the solution is not really nice. I can kind of understand the problem, but I would also have thought that this would be legitimate within a transaction then - only at the end of the transaction the constraints have to be correct.
Many greetings and thanks for your help,
Hauke
You have two options. Either you configure the unique constraint to be "deferrable" and also mark it as "initially deferred" so that the constraint is only enforced at transaction commit time, or you delete and re-insert the entries.
I would suggest you to use the first option if your database supports this. You didn't specify which database you are using, but PostgreSQL supports it. You'd only have to run alter table test_table alter constraint your_unique_constraint deferrable initially deferred.
I'm working on a legacy application which uses Hibernate and MySQL. In one of my DB tables, I've found duplicate foreign key constraints. Names are like the following:
FK3EBE45E8C4027E24
FK3EBE45E8F5ADD75E
Now I want to drop one index and rename another one from database only. Will there be any impact on hibernate functionalities?
No
There will not be any impact on the Hibernate code. Only when you make changes to the structure of the table - add/remove/rename a column, change the datatype, then there will be an impact as you will have to make changes to the DTO. MySQL Indexes are abstractions for Hibernate. Hibernate doesn't care whether there's an index or not. It will create a query and send to the database.
Renaming a constraint will be impact only on automatic schema update (create). Hibernate will try to delete constraint by name and generate an exception. It is not a problem (for Hibernate 5, don't know about other versions), a schema update will not stop.
If you don't use automatic schema update, you will not have any problems.
I have a table named GROUPS which has a column GROUP_ID whose values are
GRP001, GRP002, GRP003
and so on. Now every time I insert a new row I have to insert it with a
(current) GROUP_ID= (highest)GROUP_ID+ 1 , for example if highest GROUP_ID= GRP003 I have to generate a new GROUP_ID GRP004 when I insert a new row.
How can I do this using java?
I am currently using Hibernate along with Struts 2 in my program
Is there any way to deal with this using hibernate? Or will I have to write additional code to lock the table, check the db for max Id (and then increment it) and finally release the lock?
I remember solving a problem similar to this once. What I did was I create a custom primary key generator as supported by hibernate.
This guy explains it clearly here: "Custom Hibernate Primary Key Generator"
Basically you just need to implement org.hibernate.id.IdentifierGenerator and all should be set.
Just be aware that the solution implemented in the example above is database dependent. But I think sometimes common sense should prevail over overengineering.
I'm currently using ORMLite to work with a SQLite database on Android. As part of this I am downloading a bunch of data from a backend server and I'd like to have this data added to the SQLite database in the exact same format it is on the backend server (ie the IDs are the same, etc).
So, my question to you is if I populate my database entry object (we'll call it Equipment), including Equipment's generatedId/primary key field via setId(), and I then run a DAO.create() with that Equipment entry will that ID be saved correctly? I tried it this way and it seems to me that this was not the case. If that is the case I will try again and look for other problems, but with the first few passes over the code I was not able to find one. So essentially, if I call DAO.create() on a database object with an ID set will that ID be sent to the database and if it is not, how can I insert a row with a primary key value already filled out?
Thanks!
#Femi is correct that an object can either be a generated-id or an id, but not both. The issue is more than how ORMLite stores the object but it also has to match the schema that the database was generated with.
ORMLite supports a allowGeneratedIdInsert=true option to #DatabaseField annotation that allows this behavior. This is not supported by some database types (Derby for example) but works under Android/SQLite.
For posterity, you can also create 2 objects that share the same table -- one with a generated-id and one without. Then you can insert using the generated-id Dao to get that behavior and the other Dao to take the id value set by the caller. Here's another answer talking about that. The issue for you sounds like that this will create a lot of of extra DAOs.
The only other solution is to not use the id for your purposes. Let the database generate the id and then have an additional field that you use that is set externally for your purposes. Forcing the database-id in certain circumstances seems to me to be a bad pattern.
From http://ormlite.com/docs/generated-id:
Boolean whether the field is an auto-generated id field. Default is false. Only one field can have this set in a class. This tells the database to auto-generate a corresponding id for every row inserted. When an object with a generated-id is created using the Dao.create() method, the database will generate an id for the row which will be returned and set in the object by the create method. Some databases require sequences for generated ids in which case the sequence name will be auto-generated. To specify the name of the sequence use generatedIdSequence. Only one of this, id, and generatedIdSequence can be specified.
You must use either generatedId (in which case it appears all ids must be generated) or id (in which case you can set them) but not both.
How can I generate insert statements like insert into table (sequence.nextval, 'b0) using hibernate?
Hibernate currently selects the sequence.nextval value and only then it uses the value to insert the entry in the table.
Note: I'm not very fond of custom id generators.
Hibernate selects sequence.nextval because it has to return that value back to you (e.g. set ID on your entity). Unless you're doing something very esoteric I strongly doubt this has a big impact on performance (e.g. it's nothing compared to the actual insert). That said, you can look at Hibernate's sequence hi-lo generator - it would only access the sequence once in a while instead of every insert.
If you're using Oracle 10 client or above, check out sequence-identity in the most recent Hibernate versions to do what you're asking for.