Hibernate mapping with partitioned MySQL tables - mysql

I have a MySQL database where (most) tables are partitioned on a column TENANT_ID. Each table also has an ID field which uses AUTO_INCREMENT and is therefore unique across all partitions. The database primary key is a combination (ID, TENANT_ID) due to MySQL's requirement to have the partition column part of the primary key.
In my Java code I have mapped only the ID column with the #Id annotation. This was mostly to avoid the many problems around composite keys in Hibernate. The problem I am facing now is that most SQL statements generated by Hibernate only use the ID column. For example, an UPDATE statement generated by Hibernate would read as
UPDATE object SET value = ? WHERE ID = ?
However, since this query excludes any predicate on TENANT_ID, it does not take full advantage of the partitioning and will need to scan every partition until it finds the ID. I would like for the generated query to yield:
UPDATE object SET value = ? WHERE ID = ? AND TENANT_ID = ?
My question is whether or not there is an easy way to do this without having to resort to composite keys in JPA as I know many people discourage their use.

You can use an embedded entity, for instance ObjectPK that encompasses the id and EntityId. than use #EmbeddedId to reference it from the Object entity.

Related

How to achieve working with autogenerated composite keys in Hibernate + MySql - MariaDB

I've been reading some articles about usage of composite keys in MySql and found that a composite key can't own a auto_increment id column. However, I'm interested in using a similar feature. Let's explain it:
Using MariaDB 10 (InnoDB) and Hibernate 3.6.9
I want to do some of my application table fields translatable. I have thought an only table for translations should be enough. This table has a composite key which has an int value as a key for the translation and also the locale value for the concrete text. The same id and locale values can't place as entries.
So that's how the model should look like:
I don't want the translations to be loaded with each of the random entities as a Collection, I'm thinking about a method like String translationFor(Integer id, Locale loc) could do it for my current locale. However, when I save some translation Set I want to assign them the same id. Let's take this case:
Spanish: Cuchara
English: Spoon
The table should look as:
id locale translation
1 es Cuchara
1 en Spoon
But I can't tell MySql to have a composite id with auto_increment column. So, I consider I should assign it manually, performing these steps:
Build the Translation entities with the locale values
Begin a transaction in Hibernate session
Retrieve the last id value in the translations table
Assign it manually to the entities
Save them
Commit the transaction
Is it the most proper way? Am I doing it atomically?
I assume you are planning on having multiple tables needing the translation of 'spoon'? If so, let me move your focus away from id.
The translation table needs PRIMARY KEY(code, locale) where code is what you have as some_translatable_value in `random_table_1.
code could be the string (perhaps abbreviated) in your favorite language. Note that if you later change the phrasing of the text (to "silver spoon"), do not go back and change code; it can stay the same ("spoon").
I do not know whether you can achieve this in Hibernate; I am not fluent in that. (I tend to avoid 3rd party packages; they tend to get int the way.) If Hibernate forces you to have an AUTO_INCREMENT id on each table, so be it. It will be a harmless waste. You should then declare the pair (code, locale) as unique (in order to get the desired index).

Determining Primary Keys from ODBC Openedge 10.2B

I am currently in the process of implementing an ActiveRecord driver for Yii2 such that I could leverage my progress databases in my Yii application through their recommended APIs. However, because in ActiveRecord requires a primary key to be set in order to function properly (it has to know what fields are the unique identifiers of the record), i'm stuck manually setting these on every model class I create. (The driver can be viewed at https://github.com/ExchangeCore/yii2-progress-driver/tree/10.2.x)
What I'd like to know is if I have overlooked some method or SYSPROGRESS table (or other ODBC function) that might actually have what fields are primary keys. I have some hope for the SYSINDEXES table, but i'm not sure how to properly identify which index is the primary key.
_file._prime-index is the RECID of the _index record that describes the index marked as "primary".
It may, or may not, be unique (that isn't a requirement).
I'm not a SQL guy (nor do I play one on TV) but this 4GL code will find the primary index for the "activity" table (if you have such a table), tell you if it is unique and list the fields that make up the index:
find _file no-lock where _file-name = "activity".
find _index no-lock where recid( _index ) = _file._prime-index no-error.
if available _index then
do:
display _index._unique.
for each _index-field no-lock where _index-field._index-recid = recid( _index ):
find _field no-lock where recid( _field ) = _index-field._field-recid.
display _field-name.
end.
end.
Progress DBs don't have a "primary key" - the closest you can come to a PK is to use a unique index on that table.
If you're looking in the _Index table for a given _File._file-name, you want a record where _Unique is true.

how to perform update query using surrogate key

I am very new to database concepts and currently learning how to design a database. I have a table with below columns...
this is in mysql:
1. Names - text - unique but might change in future
2. Result - varchar - not unique
3. issues_id - int - not unique
4. comments - text - not unique
5. level - varchar - not unique
6. functionality - varchar - not unique
I cannot choose any of the above columns as primary keys as they might change in future. So i created a Auto-Increment id as names_id. I also have a GUI( a JTable) that shows this table and user updates Result,issues_id and comments based on the Names.Names here is a big text column. I cannot display names_id in the GUI as it does not make any sense in the GUI. Now when the user updates the database after giving inputs for column2,3,4 in the GUI i used the below query to update the database, i couldnt use names_id in where clause as the Jtable's row_id does not match with the names_id because not all the rows are loaded onto JTable.
update <tablename> set Result=<value>,issues_id=<value>,comments=<value>
where Names=<value>;
I could get the database updated but i want to know if its ok to update the database without even using the PK. how efficient is this? what purpose does the surrogate key serve here?
It is perfectly acceptable to update the database using a where condition that doesn't reference the primary key.
You may want to learn about indexes and constraints, though. You query could end up updating more than one row, if multiple rows have the same name. If you want to ensure that they are unique, then you can create a unique constraint on the column.
A primary key always creates an index on that column. This index makes access fast. If there is no index on name, then the update will need to scan the entire table to look at all names. You can make this faster by building an index on the field.

How to restrict a column value in SQLite / MySQL

I would like to restrict a column value in a SQL table. For example, the column values can only be "car" or "bike" or "van". My question is how do you achieve this in SQL, and is it a good idea to do this on the DB side or should I let the application restrict the input.
I also have the intention to add or remove more values in the future, for example, "truck".
The type of Databases I am using are SQLite and MySQL.
Add a new table containing these means of transport, and make your column a foreign key to that table. New means of transport can be added to the table in future, and your column definition remains the same.
With this construction, I would definitively choose to regulate this at the DB level, rather than that of the application.
For MySQL, you can use the ENUM data type.
column_name ENUM('small', 'medium', 'large')
See MySQL Reference: The ENUM Type
To add to this, I find it's always better to restrict on the DB side AND on the app side. An Enum plus a Select box and you're covered.
Yes, it is recommended to add check constraints. Check constraints are used to ensure the validity of data in a database and to provide data integrity. If they are used at the database level, applications that use the database will not be able to add invalid data or modify valid data so the data becomes invalid, even if the application itself accepts invalid data.
In SQLite:
create table MyTable
(
name string check(name = "car" or name = "bike" or name = "van")
);
In MySQL:
create table MyTable
(
name ENUM('car', 'bike', 'van')
);
You would use a check constraint. In SQL Server it works like this
ALTER TABLE Vehicles
ADD CONSTRAINT chkVehicleType CHECK (VehicleType in ('car','bike','van'));
I'm not sure if this is ANSI standard but I'm certain that MySQL has a similar construct.
If you want to go with DB-side validation, you can use triggers. See this for SQLite, and this detailed how-to for MySQL.
So the question is really whether you should use Database validation or not. If you have multiple clients -- whether they are different programs, or multiple users (with possibly different versions of the program) -- then going the database route is definitely best. The database is (hopefully) centralized, so you can decouple some of the details of validation. In your particular case, you can verify that the value being inserted into the column is contained in a separate table that simply lists valid values.
On the other hand, if you have little experience with databases, plan to target several different databases, and don't have the time to develop expertise, perhaps simple application level validation is the most expedient choice.
To add some beginner level context to the excellent answer of #NGLN above.
First, one needs to check the foreign key constraint is active, otherwise sqlite won't limit to the input to the column to the reference table:
PRAGMA foreign_key;
...which gives a response of 0 or 1, indicating on or off.
To set the foreign key constraint:
PRAGMA foreign_keys = ON;
This needs to be set to ensure that sqlite3 enforces the constraint.
I found it simplest to just set the primary key of the reference table to be the type. In the OP's example:
CREATE TABLE IF NOT EXISTS vehicle_types(
vehicle_type text PRIMARY KEY);
Then, one can insert 'car', 'bike' etc into the vehicle_types table (and more in the future) and reference that table in the foreign key constraint in the child table (the table in which the OP wished to reference the type of vehicle):
CREATE TABLE IF NOT EXISTS ops_original_table(
col_id integer PRIMARY KEY,
...many other columns...
vehicle_type text NOT NULL,
FOREIGN KEY (vehicle_type) REFERENCES vehicle_types(vehicle_type);
Outwith the scope of the OP's question but also take note that when setting up a foreign key constraint thought should be given to what happens to the column in child table (ops_original_table) if a parent table value (vehicle_types) is deleted or updated. See this page for info

Problem with hibernate trigger-generated ids (MySQL)

I'm using before and after insert triggers to generate ids (primary key) of the form "ID_NAME-000001" in several tables. At the moment, the value of the hibernate generator class of these pojos is assigned. A random string is assigned to the object to be persisted and when it's inserted by hibernate, the trigger assigns a correct id value.
The problem with this approach is that I'm unable to retrieve the persisted object because the id only exists in the database, not in the object I just saved.
I guess I need to create a custom generator class that could retrieve the id value assigned by the trigger. I've seen an example of this for oracle (https://forum.hibernate.org/viewtopic.php?f=1&t=973262) but I haven't been able to create something similar for MySQL. Any ideas?
Thanks,
update:
Seems that this is a common and, yet, not solved problem. I ended up creating a new column to serve as a unique key to use a select generator class.
Hope this won't spark a holy war for whether using surrogate key or not. But it's time to open the conversation here.
Another approach would be just, use the generated key as surrogate key and assign a new field for your trigger assigned id. The surrogate key is the primary key. You have the logically named key (such as the "ID_NAME-000001" in your example). So your database rows will have 2 keys, the primary key is surrogate key (could be UUID, GUID, running number).
Usually this approach is preferable, because it can adapt to new changes better.
Say, you have these row using surrogate key instead of using the generated id as natural key.
Surrogate key:
id: "2FE6E772-CDD7-4ACD-9506-04670D57AA7F", logical_id: "ID_NAME-000001", ...
Natural key:
id: "ID_NAME-000001", ...
When later a new requirement need the logical_id to be editable, auditable (was it changed, who changed it when) or transferable, having the logical_id as primary key will put you in trouble. Usually you cannot change your primary key. It's horribly disadvantage when you already have lots of data in your database and you have to migrate the data because of the new requirement.
With surrogate key solution, it'll be easy, you just need to add
id: "2FE6E772-CDD7-4ACD-9506-04670D57AA7F", logical_id: "ID_NAME-000001", valid: "F", ...
id: "0A33BF97-666A-494C-B37D-A3CE86D0A047", logical_id: "ID_NAME-000001", valid: "T", ...
MySQL doesn't support sequence (IMO autoincrement isn't comparable to sequence). It's different from Oracle/PostgreSQL's sequence. I guess that's the cause why it's difficult to port the solution from Oracle database to MySQL. PostgeSQL does.