Hibernate mapping a composite key with null values - java

With Hibernate, can you create a composite ID where one of the columns you are mapping to the ID can have null values?
This is to deal with a legacy table that has a unique key which can have null values but no primary key.
I realise that I could just add a new primary key column to the table, but I'm wondering if there's any way to avoid doing this.

No. Primary keys can not be null.

You wont get error but Hibernate wont be able to map those rows with NULL value for composite column to your Entity. That means you get entity with NULL values in result.

Unfortunatly, no. I either had to use a workaround:
I used composit Id for a view(! not table) where rows can be identified by 2 cols exactly (A, B). Although one of the cols (B) can have null values as well as positive integers.
So my workaround is that i created a new col in the view: "BKey" and my view is written as if B is null then value of BKey is -1 else BKey = B. (Only positive integers occour in B and null). I also changed my composit id implementation to use BKey instead of B.
Hope it helps for somebody..

This is not advisable. Could you use a view and map that instead? You could use COALESCE to supply a default if you are stuck with legacy data. We had lots of trouble with composite keys and I imagine null values will cause even more issues.

For composite keys (assumed that database allows nulls in PKs) you can have maximum number_of_cols^2 - 1 entries containing nulls, (for example for composite key of 2 columns you can have 3 rows having in their primary key null, the fourth is the PK without nulls).

Why would you want to do that? Your composite ID should map the primary key of your table, and it doesn't sound wise to put null values in a key, does it?
EDIT: Hibernate does not allow to do so; you might put the property outside the key and tweak the DAO a little to take the field into account wherever necessary

Related

JAVA: concurrent sequence

in my application I have a table to store User informations as Name, Surname, Address, Type (Employee, Manager, Administrator), UniqueNumber.
I need to store in field UniqueNumber for each record in the table User a sequence number between 1 and 100 univocal for Type: so can exist a record with Type = 'Employee' and UniqueNumber=1 and a record with Type='Manager' and UniqueNumber=1 but cannot exist another record with Type='Employee' and UniqueNumber=1.
I was thinking to use a sequence of the database for each Type. This help me to manage concurrency where more users can be inserted a record at the same time.
Is this a good solution?
Is there an alternative?
I see AtomicInteger class but I don't understand how to use it for each Type of my table.
Thanks.
AtomicInteger just provides a thread-safe usage of Integer values.
Your problem has little to do with Java, unless you're creating every single User entity in one pass. If that's the case, you can declare 3 int values and increment them for each entity you're creating (or 3 static or local AtomicInteger values if you need to handle concurrency).
Best way to do it would be to create one sequence per Type, or get the next available value for a given Type using a Max query. --> Depends on your needs
What happens when you have more than 100 values for a Type ?
We can create such code in java, but that's unnecessarily complicated. It involves a Map and AtomicInteger() and you'd use the incrementAndGet() method quite frequently.
On the other hand, you can create two tables. One for UserTypes, another for users. Then the users table can have a foreign key to the types. Problem solved on the database level. This is cool because the database will enforce this constraint.
This is how you can do it with MS SQL Server:
CREATE TABLE UserTypes (
ID INT Identity(1, 1) NOT NULL,
TypeName VARCHAR(128) NOT NULL,
PRIMARY KEY (ID)
)
CREATE TABLE Usr (
ID INT Identity(1, 1) NOT NULL,
TypeId INT NOT NULL,
MoreData VARCHAR(256) NULL,
CONSTRAINT fk_types FOREIGN KEY (TypeId) REFERENCES UserTypes (ID)
)

Inherited table with unique constraint is not generated as an UpdatableRecord

I have the following table definitions:
CREATE TABLE parent
(
id bigserial NOT NULL,
info text,
member_uuid uuid NOT NULL DEFAULT uuid_generate_v4(),
CONSTRAINT parent_pkey PRIMARY KEY (id)
);
CREATE TABLE child
(
-- Inherited from table parent: id bigint NOT NULL DEFAULT nextval('parent_id_seq"::regClass),
-- Inherited from table parent: info text
-- Inherited from table parent: member_uuid uuid NOT NULL DEFAULT uuid_generate_v4(),
member_info text,
CONSTRAINT child_member_uuid_unique UNIQUE (member_uuid)
)
INHERITS (parent);
I wish to generate the second table as a JOOQ POJO and be able to manipulate it. In particular, I wish to be able to do the following:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.insert();
record.getMemberUuid(); // autogenerated upon insert
However, in the above case with the given definition, JOOQ generates the following POJO:
public class ChildRecord extends TableRecordImpl<ChildRecord> implements Record4<Long, String, UUID, String>
Which is not an UpdatableRecord. This means that I am unable to call refresh() on this record and that the autogenerated UUID value is not available:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.insert();
record.getMemberUuid(); // null
There is a workaround, which is a bit dirty:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.setMemberUuid(UUID.randomUUID())
.insert();
record.getMemberUuid(); // available
However, there is no guarantee that the uuid_generate_v4() implementation is always going to be exactly the same as the java UUID.randomUUID() implementation.
Is this behaviour 'as expected'? Is there a workaround aside from what I have mentioned above?
After some more research, I read through the JOOQ docs, which state:
Any Record can be updatable, if
it represents a record from a table or view - a TableRecord
its underlying table or view has a "main unique key", i.e. a primary key or at least one unique key
As far as I can see, condition one is not fulfilled by my child table (due to the lack of a PK), while condition two is fulfilled. By the wording of the docs, I can surmise that both of these need to be true for the Record to be an UpdatableRecord. Insofar - this JOOQ behaviour is correct and as-expected.
Unfortunately this does not solve the issue of using values that should be autogenerated. One possible workaround might be to use an SQL statement like:
dslContext.update.<...>.setMemberUuid(select("select uuid_generate_v4()")).<...>
though this is extremely cumbersome and rather error-prone, and most likely will not solve the issue of the value not being refreshed. For now I will have to live with Java's UUID.randomUUID() method, until something better comes along.

jOOQ - update record based on unique key (not primary key)

I am using jOOQ to generate POJOs for my database tables. This works great.
I have a table with a primary key (identifier) and a unique key (name). When updating the record, jOOQ uses the primary key.
I would like to update the record by using the unique key instead of the primary key.
https://github.com/jOOQ/jOOQ/blob/master/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java
#Override
public final int update() {
return update(fields.fields.fields);
}
#Override
public int update(Field<?>... storeFields) throws DataAccessException, DataChangedException {
return storeUpdate(storeFields, getPrimaryKey().getFieldsArray());
}
In essence, I want to call storeUpdate with another key (second parameter). I tried extending the generated record, but storeUpdate is private.
Is there another way to update a record? I could first select the identifier before update(), but it introduces an extra query, which I would like to avoid.
From the comments, I understand that you want to:
Use the generated records as "ActiveRecords" holding data that is going to be stored / updated into a table
Use arbitrary "key" information as selective criteria for your update statement
There are two ways you can do this with jOOQ:
1. Override the primary key information in the code generator
You can specify a regular expression matching unique key names in your database, which should override primary keys in generated code:
<!-- All (UNIQUE) key names that should be used instead of primary keys on
generated UpdatableRecords, to be used with
- UpdatableRecord.store()
- UpdatableRecord.update()
- UpdatableRecord.delete()
- UpdatableRecord.refresh()
If several keys match, a warning is emitted and the first one encountered
will be used.
This flag will also replace synthetic primary keys, if it matches. -->
<overridePrimaryKeys>MY_UNIQUE_KEY_NAME</overridePrimaryKeys>
Note that this solution will affect all the calls to store(), update(), etc. From your comments, this might not be the desired behaviour... For more information, see the jOOQ manual
2. Use a regular UPDATE statement
You can pass the whole UpdatableRecord to an UPDATE statement and specify the selection criteria explicitly, such as:
MyTableRecord record = ...;
DSL.using(configuration)
.update(MY_TABLE)
.set(record)
.where(MY_TABLE.NAME.eq(record.getName())
.execute();

How to make the primary key in JDBC MYSQL case sensitive?

I have a primary key defined as a string value. So two tuples with primary keys as "Hello" and "hello" cant exist because the primary key is case insensitive.
What will be the sql statement to create a table whose primary key is case insensitive?
You basically need a check constraint that uses a sub query. But since sub-queries are not permitted in check constraints you can write a function that runs the query and returns a
result: (Assuming your PK is called ID)
create or replace
FUNCTION check_id(ID_INPUT IN VARCHAR2)
RETURN NUMBER
IS count_id NUMBER;
Begin
SELECT count(*) INTO count_id FROM table WHERE UPPER(ID_INPUT) = UPPER(ID);
Return(Count_Id);
END;
Then use this function in your check constraint for your primary key
CHECK check_id(ID) = 0
Try this and let me know. Have not verified it for syntax.
Having a user defined function called by a constraint is one way to go. The drawbacks are that a complete table scan must be performed every time a row is inserted -- or whenever the key field is updated, I suppose, but that shouldn't be happening too often. Also, developing a udf that can be referenced by a constraint is not always easy. Even when it's allowed, the function has to meet strict requirements about being deterministic and not altering the data state. Some DBMSs make this relatively easy, others...do not.
Another option would be to define a "shadow" field. This field, populated by insert and update trigger(s), would be the contents of the ID field converted to all one case, upper or lower. A unique constraint on the shadow field would then serve the purpose you need. This could also be implemented as a computed column, where they exist, but triggers would be better. In case of duplicates, the triggers could then intercept the exception and throw their own, with the ID field instead of the shadow named as the culprit.
The drawback to this method is that you've doubled the space needed to contain your key data. That may or may not be significant.
Interestingly, Oracle 12c has introduced the "invisible" column. It acts just like any other column except it must be referenced explicitly, it doesn't show up in select * for example. This would be a perfect use for such an invisible column.

Hibernate - Get a row with only one value of a composite primary key?

I have a table (Oracle database) that looks like:
CREATE TABLE example
(
idEx INTEGER,
idAdh INTEGER,
date DATE,
PRIMARY KEY (idEx, idAdh)
)
I have generated the corresponding classes in Netbeans and I have two classes created for this table: Example.java and ExampleId.java, this last one containing the two values of my primary key.
Now let's say I have some records here and I would like to delete one using only one value of the primary key (idEx for example, which is unique too). So first I need to get that row, but I can't find a way to do this. Would it be possible to do something like this?
Example ex = (Example) session.get(Example.class, new ExampleId(?, idEx));
I'd need something to replace that ? that would act as a wildcard.
Or maybe this is absolutely not the way to go and in this case I'd really appreciate some advices.
You should be able to get the ex with a HQL query.
There is no way to get this without an HQL/JPQL/Criteria query, for good reason.
The get method returns a single object and automatically generates a query like select * from table where key = :key.
The only way you can guarantee that this query returns exactly one row is when you specify the entire key. If you don't have the full PK object available, the query will return a list of objects, at which point get is not appropriate anymore.

Categories

Resources