Multiple join associations in hibernate - database

My question is related to database design and also how to model that design in Hibernate. I have two tables with the following primary keys:
BLOCK (BLOCK_ID)
BLOCK_SHP (BLOCK_ID, SHAPE_VERSION)
BLOCK to BLOCK_SHP is a one-to-many relationship as a single block can have many different versioned shapes associated with it. So far so good.
The second association is that I also want to be able to get the current shape for the Block. To do this, I have added another attribute to the BLOCK table:
CUR_SHAPE_VERSION
BLOCK_ID and CUR_SHAPE_VERSION now form a foreign key to the BLOCK_SHP table on BLOCK_ID, SHAPE_VERSION. Each block may have 0 or 1 current shapes.
In Hibernate, I have set this second association up in the following way:
#OneToOne( cascade = CascadeType.ALL, optional = true )
#NotFound( action = NotFoundAction.IGNORE )
#JoinColumns( {
#JoinColumn( name = "BLOCK_ID", referencedColumnName = "BLOCK_ID", insertable = false, updatable = false ),
#JoinColumn( name = "CUR_SHAPE_VERSION", referencedColumnName = "SHAPE_VERSION", insertable = false, updatable = false ) } )
public BlockShape getCurrentShape() {
return currentShape;
}
The #NotFound annotation was required because Hibernate was having trouble dealing with nullable one-to-one associations. If it doesn't find the association it ignores it instead of throwing an error.
This isn't very satisfying to me though, because it means that Hibernate isn't really aware of the proper relationship between the entities. For example, if I query for currentShape is not null, Hibernate does not know how to perform this query properly - it is querying on block_id is not null or cur_shape_version is not null.
So I guess I have a few questions. First, is there a better way to model this second association in the database? Second, is there a better way in Hibernate to set up the annotations for it to better understand the relationship and be able to properly query on the shape table?
Thanks for any help.

The easiest way is to use a surrogate primary key for the Shape entity. The tables would look like this:
BLOCK (BLOCK_ID primary key, CURRENT_SHAPE_ID foreign key references SHAPE.SHAPE_ID)
SHAPE (SHAPE_ID primary key, SHAPE_VERSION, BLOCK_ID foreign key references BLOCK.BLOCK_ID)
The use of composite keys is discouraged by Hibernate, for good reasons (and the problem you're having is just one of them).

Related

Postgresql inheritance based database design

I'm developing a simple babysitter application that has 2 types of users: a 'Parent' and the 'Babysitter'. I'm using postgresql as my database but I'm having trouble working out my database design.
The 'Parent' and the 'Babysitter' entities have attributes that can be generalized, for example: username, password, email, ... Those attributes could be
placed into a parent entity called 'User'. They both also have their own attributes, for example: Babysitter -> age.
In terms of OOP things are very clear for me, just extend the user class and you are good to go but in DB design things are differently.
Before posting this question I roamed around the internet for a good week looking for insight into this 'issue'. I did find a lot of information but
it seemed to me that there was a lot a disagreement. Here are some of the posts I've read:
How do you effectively model inheritance in a database?: Table-Per-Type (TPT), Table-Per-Hierarchy (TPH) and Table-Per-Concrete (TPC) VS 'Forcing the RDb into a class-based requirements is simply incorrect.'
https://dba.stackexchange.com/questions/75792/multiple-user-types-db-design-advice:
Table: `users`; contains all similar fields as well as a `user_type_id` column (a foreign key on `id` in `user_types`
Table: `user_types`; contains an `id` and a `type` (Student, Instructor, etc.)
Table: `students`; contains fields only related to students as well as a `user_id` column (a foreign key of `id` on `users`)
Table: `instructors`; contains fields only related to instructors as well as a `user_id` column (a foreign key of `id` on `users`)
etc. for all `user_types`
https://dba.stackexchange.com/questions/36573/how-to-model-inheritance-of-two-tables-mysql/36577#36577
When to use inherited tables in PostgreSQL?: Inheritance in postgresql does not work as expected for me and a bunch of other users as the original poster points out.
I am really confused about which approach I should take. Class-table-inheritance (https://stackoverflow.com/tags/class-table-inheritance/info) seems like the most correct in
my OOP mindset but I would very much appreciate and updated DB minded opinion.
The way that I think of inheritance in the database world is "can only be one kind of." No other relational modeling technique works for that specific case; even with check constraints, with a strict relational model, you have the problem of putting the wrong "kind of" person into the wrong table. So, in your example, a user can be a parent or a babysitter, but not both. If a user can be more than one kind-of user, then inheritance is not the best tool to use.
The instructor/student relationship really only works well in the case where students cannot be instructors or vice-versa. If you have a TA, for example, it's better to model that using a strict relational design.
So, back to the parent-babysitter, your table design might look like this:
CREATE TABLE user (
id SERIAL,
full_name TEXT,
email TEXT,
phone_number TEXT
);
CREATE TABLE parent (
preferred_payment_method TEXT,
alternate_contact_info TEXT,
PRIMARY KEY(id)
) INHERITS(user);
CREATE TABLE babysitter (
age INT,
min_child_age INT,
preferred_payment_method TEXT,
PRIMARY KEY(id)
) INHERITS(user);
CREATE TABLE parent_babysitter (
parent_id INT REFERENCES parent(id),
babysitter_id INT REFERENCES babysitter(id),
PRIMARY KEY(parent_id, babysitter_id)
);
This model allows users to be "only one kind of" user - a parent or a babysitter. Notice how the primary key definitions are left to the child tables. In this model, you can have duplicated ID's between parent and babysitter, though this may not be a problem depending on how you write your code. (Note: Postgres is the only ORDBMS I know of with this restriction - Informix and Oracle, for example, have inherited keys on inherited tables)
Also see how we mixed the relational model in - we have a many-to-many relationship between parents and babysitters. That way we keep the entities separated, but we can still model a relationship without weird self-referencing keys.
All the options can be roughly represented by following cases:
base table + table for each class (class-table inheritance, Table-Per-Type, suggestions from the dba.stackexchange)
single table inheritance (Table-Per-Hierarchy) - just put everything into the single table
create independent tables for each class (Table-Per-Concrete)
I usually prefer option (1), because (2) and (3) are not completely correct in terms of DB design.
With (2) you will have unused columns for some rows (like "age" will be empty for Parent). And with (3) you may have duplicated data.
But you also need to think in terms of data access. With option (1) you will have the data spread over few tables, so to get Parent, you will need to use join operations to select data from both User and Parent tables.
I think that's the reason why options (2) and (3) exist - they are easier to use in terms of SQL queries (no joins are needed, you just select the data you need from one table).

Alternate design approach to #OneToMany unidirectional mapping not working in OpenJPA

Hello database experts,
Consider the following tables:
CREATE TABLE customers (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
order_id INTEGER NOT NULL,
CONSTRAINT customers_ibfk_1 FOREIGN KEY (order_id) REFERENCES orders (id) ON DELETE CASCADE,
);
CREATE TABLE orders (
id INTEGER NOT NULL PRIMARY KEY,
date VARCHAR(100) NOT NULL,
...
);
Since most of my queries and needs in the application just require accessing the orders associated with a customer, I decided to go for a unidirectional One-to-many mapping from Customers to Orders, as multiple orders can be associated with a customer. I arranged the entity classes as follows:
public class Customer implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "id", referencedColumnName = "order_id")
private Set<Order> orders;
....
}
It compiles fine using JPA 2.0 and OpenJPA 2.4.0. However, throws the following exception at runtime:
...
nested exception is <openjpa-2.4.0-r422266:1674604 fatal general error> org.apache.openjpa.persistence.PersistenceException: The transaction has been rolled back. See the nested exceptions for details on the errors that occurred.] with root cause
org.apache.openjpa.persistence.ArgumentException: You cannot join on column "customers.order_id". It is not managed by a mapping that supports joins.
When I looked around, looks like it is known bug: https://issues.apache.org/jira/browse/OPENJPA-1607 .
Did I miss anything here or does this mapping look okay? To get around this issue, I have 2 approaches as far as I can see:
Make the mapping bi-directional. However, as I read, in OneToMany bidirectional mapping, the ManyToOne is the owner. So in this case, the orders table will be the owner, which really isn't the case from a design perspective. There will be no orders, without a customer.
Add a ManyToOne uni-directional mapping from orders to customers table and for any queries for all the orders for a particular customer, just query the orders table with the customer id. Of course, this will mean multiple queries for what should have been a single query.
So my question around the design is: which approach do you think is cleaner and more efficient? Is there a better different approach altogether? Is there any performance or any other benefits of using unidirectional mapping instead of bidirectional mapping? I looked around, but could not find many articles on it. Not sure if I missed it. If there are not many benefits, then I may be better off with approach 1.
I apologize if I missed something. Any pointers are greatly appreciated. Thank you for your time in advance.
thanks,
Alice
Got this working, posting the answer as it may help out some one else.
Turns out one-to-many unidirectional mapping does work in OpenJPA as long as you specify a Join table. I too was seeing the issue as specified in the bug: https://issues.apache.org/jira/browse/OPENJPA-1607 . However, once I added a Join table, it worked like a charm. Of course, it does mean that I will have to add an extra table, but it greatly reduces the amount of code and error for updates and deletes. Once we get to the performance, I will see how it performs. But for now, this is it for me. Below is the snippet:
public class Customer implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "customers_orders",
joinColumns = { #JoinColumn(name = "customer_id", referencedColumnName = "id") },
inverseJoinColumns = { #JoinColumn(name = "order_id", referencedColumnName = "id") })
private Set<Order> orders;
....
}

Updating rows in a table with a composite, non-integer primary key?

I am using peewee ORM and have a table with a composite, non-integer primary key. The key is composed of a timestamp and an integer foreign key field. An example table and the related model are shown below. I have many tables like this for different data sets I'm working with.
class fof2_site_hourly_manual(Base):
descriptor = TextField(null=True)
qualifier = TextField(null=True)
source = ForeignKeyField(db_column='source_id', rel_model=ionosonde)
valid_time = DateTimeField()
value = FloatField(null=True)
class Meta:
db_table = 'fof2_site_hourly_manual'
primary_key = CompositeKey('valid_time','source')
class ionosonde(Base):
name = TextField()
latitude = FloatField()
longitude = FloatField()
instrument_type = TextField()
class Meta:
db_table = 'ionosonde'
Now, I can insert rows with no problem using either the save() method of an instance or the class.insert().execute() approach.
However, I can't update rows by any means I've found. Using either save() or class.update().execute() I get 'duplicate primary key' errors. Fine, of course there's a duplicate, that's why I'm trying to update.
It gets worse, I get the same error just trying to examine a row using class.get(). I can read rows using select().
Is this a known limitation in peewee? The docs do state that support for composite primary keys is pretty basic, but don't explicitly state what the limitations are.
Edit: Here are all the things I have tried so far. I have the fields and values of the product I want to insert or update a row for in a dictionary, lets call it data. For inserts:
obj = class(**data)
obj.save(force_insert=True)
or
class.insert(**data).execute()
both work. However, if the row already exists (the primary key matches the relevant fields in data) then I've tried
obj = class(**data)
obj.save()
and
class.update(**data).execute()
this last one is probably not expected to work (no where clause) but I've included in for completeness in terms of what I've tried. In desperation, I attempted to implement a select, delete then insert loop, but that also fails since:
obj = class.get(**data)
and
obj = class.get_or_create(**data)
also give duplicate key errors if the row exists! That one really bakes my noodle, I'm not even trying to write to the DB!
I can read existing data out using
query = class.select()
and constructing where clauses using the known primary key fields.
Edit:
Ha! Just tried using select() to instantiate an object for a given row, then use delete_instance() to delete it (to then re-insert with the updated data). However, it deleted the entire contents of the table! It really seems as though peewee does not support composite keys, or am I doing something very wrong?

Grails domain without primary key

Anybody knows how to map grails domain class to MSSQL entity witch has not primary key
class BRCategoryInt {
String lang
String name
static hasMany = [category: BRCategory]
static constraints = {
}
static mapping = {
table "brCategoryInt"
version false
//id column: ""
category column: "CategoryId"
lang column: "Lang"
name column: "Name"
}
}
In legacy database we have not primary key, just have an one FK CategoryId.
Any help will be very appreciated.
You should really always have a primary key on your data and I would recommend adding one just to keep everyone happy. If you cannot simply add a auto-increment id to your table you could use a composite key. See documentation here. If you cannot do this either then I would consider re-thinking how youe data is laid out.
You cannot map such domain in Grails. To read/write such legacy tables try groovy Sql.
It is my understanding that in theory it is possible to map to a table without a primary key, however I have yet to see it actually done. I have struggled with attempting it for days with nothing to show.
Short answer: Not possible in the current version of Grails.

On a database level, what is the difference between [Django] OneToOneFiled and ForeignKey(Model, unique = True)

Both seems to be generating integer NOT NULL UNIQUE REFERENCES databaase columns.
Edit: My question is only about at the database level. (Not in the Django ORM.)
ForeignKey fields should be used for 1 to n relationships and OneToOneField should be used to 1 to 1 relationships.
On database level, the foreign key is unique for OneToOneFields and that's not the case for ForeignKeys.
Your answer is on the official documentation.
Basically, the difference is that when you try to access the ForeignKey from your object, you'll get another object, and not a queryset as you would in the ForeignKey.
From the docs:
class OneToOneField(othermodel[, parent_link=False, **options])
A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the "reverse" side of the relation will directly return a single object.

Resources