JPA/JPQL insertion & fetch in one - mysql

I have a table with an INT primary-key that is autoincremented.
I'm looking to get the value of
this key-field for the row I just inserted.
I can do that
1) by using LAST_INSERT_ID()
or
2) merely querying it by the value I inserted.
However, there "feels" to be a more direct way of retrieving that value.
In (1) above, it is giving me the value on the last inserted row-- not necessarily the one i inserted.
In (2), still there is an extra go-between.
I'm using JPA/JPQL on a MySQL database. JPQL does not have INSERT-- I can only do the insertion
directly on the EntityManager. So, I can't combine a query to get the field value. I only can do a query on the same transaction as I've done the insertion is on.
Is there a way of getting around this-- getting the value of the key-field on the row yi've just entered?
//=============================
EDIT:
I am looking to find out how to get the new, autoincremented key once it is there. I'm aware that the key value is incremented & assigned automatically without me having to do anything. I need that key value, because I have a use for it elsewhere.

JPA has support for autogenerated identifiers (which BTW, are the recommended strategy). Just annotate your ID field with
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
and JPA will know that the ID is generated by the database, using an auto_increment column, and will get the last inserted ID for you, transparently.

Related

Can Autoincrement field ever use the same value twice?

I have a table1 with an id field, type AutoIncrement. I need to copy the entire record from table1 into table2 if there is no record with the same id in table2. Then I delete the record from table1.
I need to know that if table1 gets new records, the id field will never be a number that was ever used before. Does this happen automatically, or do I need to do something to ensure this?
I tried deleting some records and adding new ones, and it really didn't use the same id, but I'm not sure that this is what always happens.
It is possible to duplicate numbers in autoincremet field quite easy, but normally applications don't work this way.
Access remembers last inserted value in autoincrement field and uses it for calculating next value. You cannot insert particular value into autoincrement field using table designer or recordset in VBA, but it's possible if you use INSERT SQL statement. So, if autoincrement field has no unique index, you can insert any value. Also if you insert value less than maximum existing number, Access will generate duplicates automatically.
So I would not recommend rely on unique autoincrement numbers without unique index.
INSERT SQL can be used for resetting numeration without dropping field/table, just run query like this in query builder or using VBA:
INSERT INTO Table1 ( id ) SELECT 1;
This is table with autoincrement field ID I just created:
it is really so, Auto-increment fields in MS Access are always incremental, even if records are deleted, database compacted, etc.
The proposed number can be reset deleting the auto-increment field, perform the copy of the table and then adding the auto-increment field again.
Auto increment never uses the same # even though it's deleted from the table.
It requires complete reset so that it will start from the base and create new #.

unique id without auto_increment

I have an existing schema with a non-auto-incrementing primary key. The key is used as a foreign key in a dozen other tables.
I have inherited a program with major performance problems. Currently, when a new row is added to this table, this is how a new unique id is created:
1) a query for all existing primary key values is retrieved
2) a random number is generated
3) if the number does not exist in the retrieved values, use it, otherwise goto (2)
The app is multi-threaded and multi-server, so simply grabbing the existing ids once at startup isn't an option. I do not have unique information from the initiating request to grab and convert into a pseudo-unique value (like a member id).
I understand it is theoretically possible to perform surgery on the internals to add autoincrementing to an existing primary key. I understand it would also be possible to systematically drop all foreign keys pointing to this table, then create-rename-insert a new version of the table, then add back foreign keys, but this table format is dictated by a third-party app and if I mess this up then Bad Things happen.
Is there a way to leverage sql/mysql to come up with unique row values?
The closest I have come up with is choosing a number randomly from a large space and hoping it is unique in the database, then retrying when the odd collision occurs.
Ideas?
If the table has a primary key that isn't being used for foreign key references, then drop that primary key. The goal is to make your column an auto-incremented primary key.
So, look for the maximum value and then the following should do what you want:
alter table t modify id int not null auto_increment primary key;
alter table t auto_increment = <maximum value> + 1;
I don't think it is necessary to explicitly set the auto_increment value, but I like to be sure.
I think you can SELECT MAX('strange-id-column')+1. That value will be unique and you can put that sql code inside a transaction with the INSERT code in order to prevent errors.
It seems really expensive to pull back a list of all primary key values (for large sets), and then to generate psuedo-random value and verify it's unique, by checking it against the list.
One of the big problems I see with this approach is that a pseudo-random number generator will generate the same sequence of values, when the sequence is started with the same seed value.
If that ever happens, then there will be collision after collision after collision until the sequence reaches a value that hasn't yet been used. And the next time it happens, you'd spin through that whole list again, to add one more value.
I don't understand why the value has to be random.
If there's not a requirement for pseudo-randomness, and an ascending value would be okay, here's what I would do if I didn't want to make any changes to the existing table:
I'd create another "id-generator" table that has an auto_increment column. I perform inserts to that table to generate id values.
Instead of running a query to pull back all existing id values from the existing table, I'd instead perform an INSERT into the "id-generator" table, and then a SELECT LAST_INSERT_ID() to retrieve the id of the row I just inserted, and that would use that as "generated" id value.
Basically, emulating an Oracle SEQUENCE object. It wouldn't be necessary to keep all of the rows in "id-generator" table. So, I could perform a DELETE of all rows that have an id value less than the maximum id value.
If there is a requirement for pseudo-randomness (shudder) I'd probably just attempt the INSERT as a way to find out if the key exists or not. If the insert fails due to a duplicate key, I'd try again with a different id value.
The repeated sequence from a pseudo-random generator scares me... if I got several collisions in a row... are these from a previously used sequence, or are they values from a different sequence. I don't have any way of knowing. Abandoning the sequence and restarting with a new seed, if that seed has been used before, I'm off chasing another series of previously generated values.
For low levels of concurrency (average concurrent ongoing inserts < 1) You can use optimistic locking to achieve a unique id without autoincrement:
set up a one-row table for this function, eg:
create table last_id (last_id bigint not null default 0);
To get your next id, retrieve this value in your app code, apply your newId function, and then attempt to update the value, eg:
select last_id from last_id; // In DB
newId = lastId + 1 // In app code
update last_id set last_id=$newId where last_id=$lastId // In DB
Check the number of rows that were updated. If it was 0, another server beat you to it and you should return to step 1.

How to control auto increment id?

I have an entity with a strategy to auto generate an id based on an integer column in MySQL. Things work, but while testing exceptions and related rollbacks, I noticed that MySQL does not reset last incremented value.
So a successful save produces entity id 1
An attempted save gets entity id 2 but is rolled back.
Then a successful save of a new entity gets entity id 3.
Consequently, in the table we have two records. One with id 1 and the other with id 3.
Are there any ways to control this? Basically, in the scenario I have just described, I would like to see two entities: one with id set to 1 and the other with id set to 2.
No, you can't change that. That is how it is supposed to be.
An auto-increment id has to be unique. That's all.
Auto-increment numbers have to be unique, but they don't have to be consecutive. They are monotonically increasing only as a coincidence of their implementation.
You can always insert a specific value and bypass the auto-increment mechanism. But you'd have to know what value is the "next" value. To avoid race conditions, you'd have to lock the table, query the MAX(id)+1 and then insert that value.
And that's exactly what MySQL would have to do, too, if it were to do this automatically.
The way auto-increment works now allows maximum concurrency without race conditions. So it is by design that it "loses" some values from time to time, when you rollback an INSERT, or else if you subsequently DELETE a value.
You can handle it using your own auto increment logic.
Have a Max+1 idgenerator or have a table that maintains PK auto generated IDs of such tables.
A table like this
LastKey TableName
1 TableX
5 TableY
Everytime, you will have to query from this table to get the incremented id.

mysql delete,autoincrement

I have a table in MySQL using InnoDB and a column is there with the name "id".
So my problem is that whenever I delete the last row from the table and then insert a new value, the new value gets inserted after the deleted id.
I mean suppose my id is 32, and I want to delete it and then if I insert a new row after delete, then the column id auto-increments to 33. So the serial format is broken ie,id =30,31,33 and no 32.
So please help me out to assign the id 32 instead of 33 when ever I insert after deleting the last column.
Short answer: No.
Why?
It's unnecessary work. It doesn't matter, if there are gaps in the serial number.
If you don't want that, don't use auto_increment.
Don't worry, you won't run out of numbers if your column is of type int or even bigint, I promise.
There are reasons why MySQL doesn't automatically decrease the autoincrement value when you delete a row. Those reasons are
danger of broken data integrity (imagine multiple users perform deletes or inserts...doubled entries may occur or worse)
errors may occur when you use master slave replication or transactions
and so on ...
I highly recommend you don't waste time on this! It's really, really error prone.
You have two major misunderstandings about how a relational database works:
there is no such thing as the "last row" in a relational database.
The ID (assuming that is your primary key) has no meaning whatsoever. It doesn't matter if the new row is assigned the 33, 35354 or 236532652632. It's just a value to uniquely identify that row.
Do not rely on consecutive values in your primary key column.
And do not try the max(id)+1 approach. It will simply not work in a system with more than one transaction.
You should stop fighting this, even using SELECT max(id) will not fix this properly when using transactional database engine like Innodb.
Why you might ask? Imagine that you have 2 transactions, A and B, that started almost at the same time, both doing INSERT. First transaction A needs new row id, and it will use it from invisible sequence associated with this table (known as AUTOINCREMENT value), say 21. Another transaction B will use another successive value (say 22) - so far so good.
But, what if transaction A rolls back? Value 21 cannot be reused, and 22 is already committed. And what if there were 10 such transactions?
And max(id) can assign the same value to both A and B, so this is not valid as well.
I suppose you mean "Whenever I delete the last row from the table", isn't it?
Anyway this is how autoincrement works. It's made to keep correct data relations. If in another table you use an id of a record that has been deleted it's more correct to get an error instead of get another record when querying that id.
Anyway here you can see how to get the first free id in a field.

How to properly clean up a table

In order to determine how often some object has been used, I use a table with the following fields;
id - objectID - timestamp
Every time an object is used, it's ID and time() are added in. This allows me to determine how often an object has been used in the last hour/minute/second etc.
After one hour, the row is useless (I'm not checking above one hour). However, it is my understanding that it is unwise to simply delete the row, because it may mess up the primary key (auto_increment ID).
So I added a field called "active". Prior to checking how often an object has been used I loop over all WHERE active=1 and set it to 0 if more than 1 hour has passed. I don't think this would give any concurrency problems between multiple users, but this leaves me with alot of unused data.
Now I'm thinking that maybe it's best to, prior to inserting new usage data, check if there is a field with active=0 and then rather than inserting a new row, update that one with the new data, and set active to 1 again. However, this would require table locking to prevent multiple clients from updating the same row.
Can anyone shed some more light on this, please?
I've never heard anywhere that deleting rows messes up primary keys.
Are you perhaps attempting to ensure that the id values automatically assigned by auto_increment match those of another table? This is not necessary - you can simply use an INTEGER PRIMARY KEY as the id column and assign the values explicitly.
You could execute an update query that match all rows older than 1 hour.
UPDATE table SET active=0 WHERE timestamp < now() - interval 1 hour