hibernate insert into select - java

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.

Related

Hibernate: Update table generator

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.

Generate several IDs at once in Firebird for Prepared Statement batches

I'm using SELECT GEN_ID(TABLE,1) FROM MON$DATABASE from a PreparedStatement to generate an ID that will be used in several tables.
I'm going to do a great number of INSERTs with PreparedStatements batches and I'm looking for a way to fetch a lot of new IDs at once from Firebird.
Doing a trigger seems to be out of the question, since I have to INSERT on other tables at another time with this ID in the Java code. Also, getGeneratedKeys() for batches seem to not have been implemented yet in (my?) Firebird JDBCdriver.
I'm answering from memory here, but I remember that I once had to load a bunch of transactions from a Quicken file into my Firebird database. I loaded an array with the transactions and set a variable named say iCount to the number. I then did SELECT GEN_ID(g_TABLE, iCount) from RDB$DATABASE. This gave me the next ID and incremented the generator by the number of records that I was going to insert. Then I started a transaction, stepped through the array and inserted the records one after the other and closed the transaction. I was surprised how fast this went. I think, at the time, I was working with about 28,000 transactions and the time was like a couple of seconds. Something like this might work for you.
As jrodenhi says, you can reserve a range of values using
SELECT GEN_ID(<generator>, <count>) FROM RDB$DATABASE
This will return a value of <count> higher than the previously generated key, so you can use all values from (value - count, value] (where ( signifies exclusive, ] inclusive). Say generator currently has value 10, calling GEN_ID(generator, 10) will return 20, you can then use 11...20 for ids.
This does assume that you normally use generators to generated ids for your table, and that no application makes up its own ids without using the generator.
As you noticed, getGeneratedKeys() has not been implemented for batches in Jaybird 2.2.x. Support for this option will be available in Jaybird 3.0.0, see JDBC-452.
Unless you are also targeting other databases, there is no real performance advantage to use batched updates (in Jaybird). Firebird does not support update batches, so the internal implementation in Jaybird does essentially the same as preparing a statement and executing it yourself repeatedly. This might change in the future as there are plans to add this to Firebird 4.
Disclosure: I am one of the Jaybird developers

Hibernate, what is the most efficient id generation strategy?

I need to insert many entities into the database via Hibernate. So, I want to find the most effective algorithm for Id generation.
Accordingly Hibernate Documentation exists four widely used generation strategies:
IDENTITY
SEQUENCE
TABLE
AUTO
I should use MySQL database, so I cannot apply SEQUENCE generation strategy. What about other strategies? What is the most efficient from performance point of view?
The best id generators in Hibernate are enhanced-table and enhanced-sequence, coupled with an appropriate optimizer, such as hilo. I have experience with enhanced-table + hilo, inserting over 10,000 records per second.
BTW the statement that "hilo needs an additional query per generated entity" is patently false: the whole point of the optimizer is to prevent this.
As you can't use SEQUENCE, and AUTO just automatically selects a supported generator algorithm out of the existing ones, you are left with IDENTITY and TABLE.
TABLE: uses a hi/lo algorithm to efficiently generate identifiers of type long, short or int, given a table and column as a source of hi values. The hi/lo algorithm generates identifiers that are unique only for a particular database. -> Means an extra query per generated entity. (This is not true if you use optimizers. Unfortunately, using no optimizer generally is the default, if no optimizer was specified.)
IDENTITY: supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. -> Performance-wise, this is the way to go, the same way you would do without Hibernate normally. Database generated, almost no overhead.
There exist more Hibernate specific generators, but they won't beat performance-wise the database generated ID. (See 5.1.2.2.1. Various additional generators in your linked document.)

How to insert a entity at the end of a table using JPA?

Hello and happy new year for everyone.
I need to insert a record at the end of a table (the table has not set autoincrement) using JPA.
I know I could get the last id (integer) and apply to the entity before insert, but how could that be done? Which way would be most effective?
There is no such thing as "the end of the table". Rows in a relational table are not sorted.
Simply insert your new row. If you need any particular order, you need to apply an ORDER BY when selecting the rows from the table.
If you are talking about generating a new ID, then use an Oracle sequence. It guarantees uniqueness.
I would not recommend using a "counter table".
That solution is either not scalable (if it's correctly implemented) or not safe (if it's scalable).
That's what sequences were created for. I don't know JPA, but if you can't get the ID from a sequence then I suggest you find a better ORM.
Well, while i do not know where the end of a table really is, JPA has a lot of options for plugging in ID generators.
One common option is to use a table of its own, having a counter for each entity you need an ID for (from http://download.oracle.com/docs/cd/B32110_01/web.1013/b28221/cmp30cfg001.htm).
#Id(generate=TABLE, generator="ADDRESS_TABLE_GENERATOR")
#TableGenerator(
name="ADDRESS_TABLE_GENERATOR",
tableName="EMPLOYEE_GENERATOR_TABLE",
pkColumnValue="ADDRESS_SEQ"
)
#Column(name="ADDRESS_ID")
public Integer getId() {
return id;
}
...other "Generator" strategies to be googled...
EDIT
I dare to reference #a_horse_with_no_name as he says he does not know about JPA. If you want to use native mechanisms like sequence (that are not available in every DB) you can declare such a generator in JPA, too.
I do not know what issues he encountered with the table approach - i know large installations running this successfully. But anyway, this depends on a lot of factors besides scalability, for example if you want this to be portable etc. Just lookup the different strategies and select the appropriate.

Alright to truncate database tables when also using Hibernate?

Is it OK to truncate tables while at the same time using Hibernate to insert data?
We parse a big XML file with many relationships into Hibernate POJO's and persist to the DB.
We are now planning on purging existing data at certain points in time by truncating the tables. Is this OK?
It seems to work fine. We don't use Hibernate's second level cache. One thing I did notice, which is fine, is that when inserting we generate primary keys using Hibernate's #GeneratedValue where Hibernate just uses a key value one greater than the highest value in the table - and even though we are truncating the tables, Hibernate remembers the prior value and uses prior value + 1 as opposed to starting over at 1. This is fine, just unexpected.
Note that the reason we do truncate as opposed to calling delete() on the Hibernate POJO's is for speed. We have gazillions of rows of data, and truncate is just so much faster.
We are now planning on purging existing data at certain points in time by truncating the tables. Is this OK?
If you're not using the second level cache and if you didn't load Entities from the table you're going to truncate in the Session, the following should work (assuming it doesn't break integrity constraints):
Session s = sf.openSession();
PreparedStatement ps = s.connection().prepareStatement("TRUNCATE TABLE XXX");
ps.executeUpdate();
And you should be able to persist entities after that, either in the same transaction or another one.
Of course, such a TRUNCATE won't generate any Hibernate event or trigger any callback, if this matters.
(...) when inserting we generate primary keys using Hibernate's #GeneratedValue (...)
If you are using the default strategy for #GeneratedValue (i.e. AUTO), then it should default to a sequence with Oracle and a sequence won't be reseted if you truncate a table or delete records.
We truncate tables like jdbcTemplate.execute("TRUNCATE TABLE abc")
This should be equivalent (you'll end-up using the same underlying JDBC connection than Hibernate).
What sequence would Hibernate use for the inserts?
AFAIK, Hibernate generates a default "hibernate_sequence" sequence for you if you don't declare your own.
I thought it was just doing a max(field) + 1 on the table?
I don't think so and the fact that Hibernate doesn't start over from 1 after the TRUNCATE seems to confirm that it doesn't. I suggest to activate SQL logging to see the exact statements performed against your database on INSERT.
The generator we specify for #GeneratedValue is just a "dummy" generator (doesn't correspond to any sequence that we've created).
I'm not 100% sure but if you didn't declare any #SequenceGenerator (or #TableGenerator), I don't think that specifying a generator changes something.
Depends on your application. If deleting rows in the database is okey, then truncate is okey, too.
As far as you don't have any Pre- or PostRemove listeners on your entities, there should be no problems.
On the other hand... is it possible that there are still entities loaded in an EntityManager at truncate time, or is this a writeonly table (like a logging table). In this case you won't have any problem at all.

Categories