How will be better, returning only generated keys of inserted entities vs returning full inserted entities? - sql

I have a question, what is the best way to return data of inserted entities with auto generated keys (IDs). For example, i need to return entities back to front after inserting them into the database. I see three ways:
Returning only generated keys and matching them to necessary entity objects in my code manually.
Returning full entities and map it into appropriate entity class.
Do not return anything and get data back by separate select query.
Case 3 seems less effective, and i can't choose between case 1 and case 2.
P.S. I'm using PostgreSQL, if it make difference.

Related

Is there a way in JOOQ to pull a number of records without multiple DB calls?

In our webapp we have a number of places where you would be updating a number of tables in one complex form/view. In raw SQL I would probably select a bunch of columns from a bunch of tables and edit that one record on the primary table as well as related parent/child tables.
In hibernate I would probably just pull a JPA entity for the main table and let hibernate fetch the parent/child relationships as I populate the view. And then later pull from my view back to the entity and call entitymanger .perist/merge.
In JOOQ I have a number of options but it appears you can pull a main record via selectFrom/fetch then use fetchChild fetchParent to pull typed related records like so...
LoadsRecord load = dslContext.selectFrom(LOADS)
.where(LOADS.ID.eq(id))
.fetchOne();
SafetyInspectionsRecord safetyInspection = load.fetchParent(Keys.LOADS__FK_SAFETY_INSPECTION);
So this way I am able to pull related records in a typesafe manner. The only annoying thing is I have to run another full query every time I call fetchParent or fetchDhild. Is there a way to eagerly fetch these all at once to avoid multiple round trips to the DB?
It is really nice to have these classes like LoadsRecord for CRUD screens, it makes updating the DB easy.
Classic approach using joins
There are various ways you can achieve materialising a to-one relationship. The simplest one being a simple JOIN or LEFT JOIN if the relationship is optional.
E.g.:
Result<?> result =
ctx.select()
.from(LOADS)
.join(SAFETY_INSPECTIONS)
.on(LOADS.SAFETY_INSPECTIONS_ID.eq(SAFETY_INSPECTIONS.ID))
.fetch();
You probably want to work with the generated records thereafter, so you can use various mapping tools to map the generic Record types to the two UpdatableRecord types for further CRUD:
for (Record r : result) {
LoadsRecord loads = r.into(LOADS);
SafetyInspectionsRecord si = r.into(SAFETY_INSPECTIONS);
}
Using nested records
Starting from jOOQ 3.15 and #11812, MULTISET and ROW operators can be used to create nested collections and records. So, in your query, you could write:
Result<?> result =
ctx.select(
row(LOADS.ID, ...),
row(SAFETY_INSPECTIONS.ID, ...))
.from(LOADS)
.join(SAFETY_INSPECTIONS)
.on(LOADS.SAFETY_INSPECTIONS_ID.eq(SAFETY_INSPECTIONS.ID))
.fetch();
That would already help map the nested data structures into the desired format. Starting from jOOQ 3.17 and #4727, you can even use table expressions directly to generate nested records:
Result<Record2<LoadsRecord, SafetyInspectionsRecord>> result =
ctx.select(LOADS, SAFETY_INSPECTIONS)
.from(LOADS)
.join(SAFETY_INSPECTIONS)
.on(LOADS.SAFETY_INSPECTIONS_ID.eq(SAFETY_INSPECTIONS.ID))
.fetch();
This new feature is definitely going to close one of jOOQ's biggest gaps. You could even simplify the above using implicit joins to this:
Result<Record2<LoadsRecord, SafetyInspectionsRecord>> result =
ctx.select(LOADS, LOADS.safetyInspections())
.from(LOADS)
.fetch();

Best practice for many to many data mapping

I am looking to find the best practice for many to many mapping in database.
For example I have two tables to be mapped. I create third table to store this mapping.
On UI I have several A to be mapped(or not) multiple with B. And I see two solutions for now:
1 - On every update for every record from A I will delete all mapped data for it and insert new data mapping.
Advantage: I store only mapped data.
Disadvantage: I need to use delete and insert statement every time.
2 - I need to add new bit column to AB table with name isMapped. And I will store all mapping for every record from A to every record from B. On save mapping action I will use only update statement.
Advantage: No need to delete and insert every time.
Disadvantage: Need to store unnecessary records.
Can you offer me best solution?
Thanks
between the 2 options you have listed I would go with option no 1, isMapped is not meaningful, if they are not mapped the records should not exists in the first place.
you still have one more option though:
DELETE FROM AB where Not in the new map
INSERT INTO AB FROM (New map) where NOT in AB
if these are a lot of maps I would delete and insert from the new mapping, otherwise I would just delete all then insert like you are suggesting.
I'd say anytime you see the second bullet point in your #2 scenario
"Need to store unnecessary records"
that's your red flag not to use that scenario.
Your data is modeled correctly in scenario 1, i.e. mapppings exist in the mapping table when there are mappings between records in A and B and mappings do not exist in the mapping table when there is not a mapping between those records in A and B.
Also, the underlying mechanics of an update statement are a delete and then an insert, so you are not really saving the database any work by issuing one over the other.
Lastly, speaking of saving the database work, don't try and do it at this stage. This is what they are designed for. :)
Implementing your data model correctly as you are in Scenario 1 is the best optimization you can make.
Once you have the basic normalized structure in place and have some test data, then you can start testing performance and refactoring if necessary. Adding indexes, changing data structures, etc.

NHibernate double fetching for non-existent many-to-one relationship

I have an entity with several Many to One relationships and I've found I can eagerly fetch them in one query like such:
public Accommodation GetEager(int id)
{
IList<Accommodation> query = NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey)
.CreateQuery(#"
select a from Accommodation as a
left join fetch a.AccommodationType
left join fetch a.AccommodationUnitType
left join fetch a.CollectionType
where a.Id = :id
")
.SetProperties(new {id})
.SetCacheable(true)
.List<Accommodation>();
return query.SingleOrDefault();
}
However, the relationships don't always exist, and I've defined the mappings like so:
mapping.References(x => x.AccommodationUnitType).NotFound.Ignore();
I've found that when a relationship doesn't exist, NHibernate generated a second SQL query looking for it, presumably because it's found that the property is null.
My first question is, how can I prevent this second sql query?
My second question is, is there an easier way to do this fetching into one query? It seems very basic behaviour that one would want to fetch everything in one query rather than a seperate query for each many-to-one relationship (which seems to be the default behaviour).
Are you sure you're using NotFound().Ignore() correctly? This setting determines what NHibernate will do if there is an invalid foreign key. In that case NotFound().Ignore() prevents throwing an EntityNotFoundException. With the setting, if the related object is not found then the value will be null. If you have referential integrity for this relationship then you do not need NotFound().Ignore().
The behavior you're seeing is apparently expected and well known and unlikely to change.
As for your second question, I would advise always starting with the default behavior of lazy loading and only optimizing with eager loads as needed for real world performance problems. Lazy loading is frequently more efficient than eager fetching because a) the object may already be in cache (no db trip required) and b) selecting by primary key is very fast. It's very possible that your query with three joins performs worse than four selects by primary key.

How can one delete an entity in nhibernate having only its id and type?

I am wondering how can one delete an entity having just its ID and type (as in mapping) using NHibernate 2.1?
If you are using lazy loading, Load only creates a proxy.
session.Delete(session.Load(type, id));
With NH 2.1 you can use HQL. Not sure how it actually looks like, but something like this: note that this is subject to SQL injection - if possible use parametrized queries instead with SetParameter()
session.Delete(string.Format("from {0} where id = {1}", type, id));
Edit:
For Load, you don't need to know the name of the Id column.
If you need to know it, you can get it by the NH metadata:
sessionFactory.GetClassMetadata(type).IdentifierPropertyName
Another edit.
session.Delete() is instantiating the entity
When using session.Delete(), NH loads the entity anyway. At the beginning I didn't like it. Then I realized the advantages. If the entity is part of a complex structure using inheritance, collections or "any"-references, it is actually more efficient.
For instance, if class A and B both inherit from Base, it doesn't try to delete data in table B when the actual entity is of type A. This wouldn't be possible without loading the actual object. This is particularly important when there are many inherited types which also consist of many additional tables each.
The same situation is given when you have a collection of Bases, which happen to be all instances of A. When loading the collection in memory, NH knows that it doesn't need to remove any B-stuff.
If the entity A has a collection of Bs, which contains Cs (and so on), it doesn't try to delete any Cs when the collection of Bs is empty. This is only possible when reading the collection. This is particularly important when C is complex of its own, aggregating even more tables and so on.
The more complex and dynamic the structure is, the more efficient is it to load actual data instead of "blindly" deleting it.
HQL Deletes have pitfalls
HQL deletes to not load data to memory. But HQL-deletes aren't that smart. They basically translate the entity name to the corresponding table name and remove that from the database. Additionally, it deletes some aggregated collection data.
In simple structures, this may work well and efficient. In complex structures, not everything is deleted, leading to constraint violations or "database memory leaks".
Conclusion
I also tried to optimize deletion with NH. I gave up in most of the cases, because NH is still smarter, it "just works" and is usually fast enough. One of the most complex deletion algorithms I wrote is analyzing NH mapping definitions and building delete statements from that. And - no surprise - it is not possible without reading data from the database before deleting. (I just reduced it to only load primary keys.)

Hibernate and IDs

Is it possible in hibernate to have an entity where some IDs are assigned and some are generated?
For instance:
Some objects have an ID between 1-10000 that are generated outside of the database; while some entities come in with no ID and need an ID generated by the database.
You could use 'assigned' as the Id generation strategy, but you would have to give the entity its id before you saved it to the database. Alternately you could build your own implementation of org.hibernate.id.IdentifierGenerator to provide the Id in the manner you've suggested.
I have to agree w/ Cade Roux though, and doing so seems like it be much more difficult than using built in increment, uuid, or other form of id generation.
I would avoid this and simply have an auxiliary column for the information about the source of the object and a column for the external identifier (assuming the external identifier was an important value you wanted to keep track of).
It's generally a bad idea to use columns for mixed purposes - in this case to infer from the nature of a surrogate key the source of an object.
Use any generator you like, make sure it can start at an offset (when you use a sequence, you can initialize it accordingly).
For all other entities, call setId() before you insert them. Hibernate will only generate an id if the id property is 0. Note that you should first insert objects with ids into the db and then work with them. There is a lot of code in Hibernate which expects the object to be in the DB when id != 0.
Another solution is to use negative ids for entities which come with an id. This will also make sure that there are no collisions when you insert an new object.