Hibernate: Automatic Versioning Nullable Column - java

I have a table which contains huge data. I want to add hibernate automatic versioning to this table. But adding a not-null column to this table for versioning is very expensive due to huge data. Is there a workaround so hibernate can work with nullable column? Currently hibernate gives NPE because it tries to increment a null value.
( As hibernate manages this internally, changing the version value on client side is out of question )
Any other versioning startegy is welcome too. Thanks in advance

If your flavour of database permits it you could use the DEFAULT option. This is against Oracle ...
SQL> create table t23 as select object_id as id from user_objects;
Table created.
SQL> desc t23
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
SQL> alter table t23 add hibernate_version_number number default 0 not null;
Table altered.
SQL> desc t23
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
HIBERNATE_VERSION_NUMBER NOT NULL NUMBER
SQL> select count(*) from t23 where hibernate_version_number = 0;
COUNT(*)
----------
504
SQL>
However, you may still want to benchmark its performance against a realistic volume of data. It may not solve your problem.

Related

How to resolve "ORA-00904: Invalid identifier insert statement" [duplicate]

This question already has answers here:
How To Handle Table Column Named With Reserved Sql Keyword?
(4 answers)
Closed 19 days ago.
I'm trying to add a column to my users table but I get an error every time I try to create a new user I end up getting the error "java.sql.SQLSyntaxErrorException: ORA-00904: "IMPORTED": invalid identifier".
In the database my column is created but I still get an error when i try to create a user.
I'm trying to create a column that receives true or false in my user table.
When I try to create a new user by jpa I end up getting an error
My Model:
#Column(name = "IMPORTED")
#Enumerated(EnumType.STRING)
private Eboolean imported;
My query:
alter table USER add IMPORTED VARCHAR2(1);
What you are saying is not (entirely) true. Table name certainly isn't user:
SQL> create table user (id number);
create table user (id number)
*
ERROR at line 1:
ORA-00903: invalid table name
Why? Because it is reserved for function that returns currently logged user:
SQL> select user from dual;
USER
--------------------------------------------------------------------------------
SCOTT
SQL>
Therefore, table name is something else; or, if it is user, then you must have created it by enclosing its name into double quotes, but then you have to use double quotes every time, matching letter case every time:
SQL> create table "user" (id number);
Table created.
Presuming that this is what you have, then:
SQL> alter table "user" add imported varchar2(1);
Table altered.
SQL>
Furthermore, error you specified is
ORA-00904: invalid identifier
If you got it when inserting a row into a table, it means that there's no such column in that table. Possible causes:
you misspelled its name
there's really no such column there
double quotes and letter case matching issue
Presuming that column exists (see above alter table), insert also works:
SQL> insert into "user" (id, imported) values (1, 0);
1 row created.
SQL> select * from "user";
ID IMPORTED
---------- ----------
1 0
SQL>

How can I access a value when inserting into a table?

I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;

How to add a field as a key to a hibernate update statement

I am using postgres 9.4 and have a date range partitioned table. The primary key on the table is id. The tables is partitioned on a field called create_date. We are inserting into the partitions using an insert trigger which looks at the create_date field and writes it into the appropriate partition.
We are using Hibernate 4.3.11 for ORM. To update data in the table we are using the EntityManager::merge method. I would like to add the create_date to the WHERE clause of the update SQL that Hibernate generates.
For example, right now my update looks like:
UPDATE my_table SET col1 = $1 where id = $2
In order to get partition pruning in my update statement I would like my query to look like this:
UPDATE my_table SET col1 = $1 where id = $2 and create_date = $3
Is there any way to accomplish this with Hibernate?

JDBC - PostgreSQL - batch insert + unique index

I have a table with unique constraint on some field. I need to insert a large number of records in this table. To make it faster I'm using batch update with JDBC (driver version is 8.3-603).
Is there a way to do the following:
every batch execute I need to write into the table all the records from the batch that don't violate the unique index;
every batch execute I need to receive the records from the batch that were not inserted into DB, so I could save "wrong" records
?
The most efficient way of doing this would be something like this:
create a staging table with the same structure as the target table but without the unique constraint
batch insert all rows into that staging table. The most efficient way is to use copy or use the CopyManager (although I don't know if that is already supported in your ancient driver version.
Once that is done you copy the valid rows into the target table:
insert into target_table(id, col_1, col_2)
select id, col_1, col_2
from staging_table
where not exists (select *
from target_table
where target_table.id = staging_table.id);
Note that the above is not concurrency safe! If other processes do the same thing you might still get unique key violations. To prevent that you need to lock the target table.
If you want to remove the copied rows, you could do that using a writeable CTE:
with inserted as (
insert into target_table(id, col_1, col_2)
select id, col_1, col_2
from staging_table
where not exists (select *
from target_table
where target_table.id = staging_table.id)
returning staging_table.id;
)
delete from staging_table
where id in (select id from inserted);
A (non-unique) index on the staging_table.id should help for the performance.

Combine two resultset from different table

My Requirement is to display some of the columns in one table and some of the columns in another table in an html table. Though it has same coloumn id , value will be different. So, I cannot match this two. My query is as follows:
SELECT time_stamp,queryresultset FROM table1 d WHERE dID = 'CP009'
AND d.time_stamp >'2011-05-01 00:00:00' AND d.time_stamp < '2011-05-01 05:00:00'
order by time_stamp
UNION ALL
SELECT time_stamp,cpuutil FROM table2 h WHERE hID='HS002'
AND h.time_stamp >'2011-05-01 00:00:00' AND h.time_stamp < '2011-05-01 05:00:00'
order by time_stamp
So, the time_stampe here I'm getting will vary just in milliseconds for both the table. But, I want it in one resultset. Though the time value varies in millisecones between the table, the number of rows will be equal. So, I have to bring this in a single resultset. I don't know whether it is possible to handle in sql query. Or I may have to try in java coding? Please guide me. Following is my sample html table.
----------------------------------------------------
Time_stamp Cpuutil Queryresultset
----------------------------------------------------
2011-03-09 12:00:00 2.3 9.8
2011-03-09 12:15:00 5.3 4.5
2011-03-09 12:30:00 4.3 9.3
2011-03-09 12:45:00 2.3 9.2
I am afraid, I have difficulties with understanding your question, but it seems to me, you are looking for something as:
SELECT table1.time_stamp t1, table1.queryresultset, table2.time_stamp t2, table2.cpuutil
FROM table1 , table2
WHERE ABS(t1-t2)<100
AND t1 >'2011-05-01 00:00:00'
AND t1 < '2011-05-01 05:00:00'
ORDER by t1
Another posibility:
`SELECT column list
FROM table1
INNER JOIN table2
ON table1.col1=table2.col2
WHERE criteria
ORDER BY column list `
Have a nice day.
This sounds like a database conception mistake. If your two tables are linked logically, they should have a physical connector. If you can refactor your database model, the best solution is to add a table table0 holding the common factors between the two tables (or just an autoincrement id), and then to add an external key to table1 and table2. You need to insert a row into table0 first, then to insert a row into table1 and one into table2 using table0's key as an external key.
If you can't refactor the tables, that's too bad. Anyway, the easiest way to do what you want is to write java code:
Open resultset1 from table 1
Open resultset2 from table 2
While resultset1 is not empty
fetch row1 from resultset1
fetch row2 from resultset2
generate html
Close resultsets
Done!
But this way is wrong and you will get problems as you advance further in your project.
Thanks for your responses. So I found out the query. It's working now.
SELECT a.cpuutil,a.hostid,a.time_stamp, b.queryresultset, b.time_stamp AS tm
FROM table1 a, table2 b
WHERE a.hID = 'hs002' AND b.dID='cp011'
AND SUBSTR(a.time_Stamp,1,15) = SUBSTR(b.time_stamp,1,15)
AND a.time_stamp > '2011-05-10 00:00:00'
AND a.time_stamp < '2011-05-10 14:00:00'

Categories